Cocoa Internals - 스위프트 타입 시스템

Swift 타입 시스템

타입 시스템은 프로그래밍 언어 작성 방식과 프레임워크의 구조를 결정하는 매우 중요한 요소다.

Swift를 비롯한 함수 중심 언어의 타입 시스템은 일반적인 것들보다 더 안전하고 세밀하다.

Swift 타입 시스템의 동작 원리와 주요한 값의 구조를 살펴보며, Swift 런타임 API와 Foundation 프레임워크의 구조를 살펴본다.

타입 시스템

Swift의 타입 시스템은 Javascript나 Python처럼 자유로운 덕 타입Duck typing 시스템이 아닌, 명시적인 타입Nominal typing 시스템이다.

프로토콜 타입을 활용하여 객체를 다이나믹하게 확장하면서 Objective-C보다 안전하게 사용할 수 있다.

Swift 타입

이름 있는 타입named type과 이름 없이 합쳐진 타입compound type이 있다.

이름 있는 타입

클래스, 열거형, 구조체, 프로토콜과 같이 타입 이름이 미리 정해진 형태를 말한다.

기존 타입을 상속하거나 확장하여 나만의 이름을 주고 새로운 타입으로 지정할 수 있다.

Objective-C의 int와 같은 기본 타입primitive type도 Swift에서는 구조체 타입으로 만들어진 이름 있는 타입이다.

이름 없이 합쳐진 타입

튜플이나 클로저 / 함수 타입으로, 이름이 따로 정해지지 않고 다른 타입들을 합쳐서 사용하는 타입이다.

(Int, (Int) -> Int) 튜플 타입의 경우, 첫 번째 인자로 Int 타입을 받고, 두 번째 인자로 Int 타입을 받아 Int 타입을 반환하는 함수 타입을 받는다.

내부 타입 유형

Swift 컴파일러는 내부적으로 세부 타입을 구분한다.

  • 내부 지정Builtin
    • BuiltinInteger / BuildtinFloat / BuiltinRawPointer / BuiltinNativeObject / BuiltinBridgeObject / BuiltinUnknownObject / BuiltinUnsafeValueBuffer
    • Object 계열의 경우 레퍼런스 타입이다.
  • 이름 지정Nominal
    • Enum / Struct / Class / Protocol
    • Class의 경우 레퍼런스 타입이다.
  • 고정 타입
    • Tuple / DynamicSelf / ProtocolComposition / Module
  • 메타 타입
    • MetaType / ExistentialMetaType
  • 저장 참조Reference Storage
    • UnownedStorage / UnmanagedStorage / WeakStorage
  • 함수Function 타입
    • Function / PolymorphicFunction / GenericFunction / SILFunction
  • 대체Substitutable
    • Archetype / GenericTypeParam / AssociatedType
  • 제네릭Generic
    • BoundGeneric / BoundGenericClass / BoundGenericEnum / BoundGenericStruct
  • 타입 미확인Unchecked
    • Error / Unresolved / UnboundGeneric
  • 문법syntax 도움
    • ArraySlice / Optional / ImplicitlyUnwrappedOptional / Dictionary
  • 컴파일러 내부 전용
    • SILFunction / SILBlockStorage / SILBox

타입 검사

Swift는 안전한 타입 언어를 표방한다. 이는 값에 대한 타입을 명확하게 구분해서 사용할 수 있음을 의미한다.

컴파일러는 컴파일하는 동안 타입 검사를 진행하여 타입이 일치하지 않음으로 발생하는 문제를 미리 발견할 수 있도록 도와준다.

타입 추론

Type Inference

타입을 지정하지 않는 경우 타입 추론을 통해 값에 대한 적절한 타입을 유추한다.

이는 HM(Hindley-Milder) 타입 추론 알고리즘을 구현하는 타입 제약Constraint을 이용하여 이루어진다.

  • 컴파일러는 문맥에 따라서 제약 사항을 수식으로 만들고, HM 타입 추론 알고리즘으로 특정 변수나 표현식 타입에 적합한지 계산한다.
  • 타입 제약은 타입 비교를 통한 등가성equality를 판단하고, 서브 타입에 대한 조건subtyping을 비교하며, 타입 사이에 전환conversion이 가능한지 판단하거나, 다른 타입을 생성하는 값으로 활용할 수 있는지construction 판단한다.
    • 프로토콜 타입의 경우 프로토콜 규칙을 따르는지conformance 확인한다.
    • 타입 변환에 사용되는 as 연산자checked cast도 제약 사항 중 하나로 계산된다.
    • 다형성을 위한 제약 사항이나 함수 오버로딩에 대한 제약 사항도 있다.

Swift의 타입 추론은 ‘제약 만들기’ -> ‘제약 계산하기’ -> ‘제약 판단하기’의 3단계로 진행된다.

  1. 제약 만들기
    • 컴파일 요소로 분석해야 하는 표현식과, 문맥 정보에서 유추해야 하는 숨겨진 표현식을 분석하여, 각 요소들의 타입 관계를 제약 사항 집합으로 만든다.
  2. 제약 계산하기
    • 위에서 만든 제약 사항을 계산하여 여러 가능성 중에서 가장 적합한 타입을 찾기 위한 준비를 한다.
  3. 제약 판단하기
    • 위에서 만든 제약 사항, 표현식과 확정적인 타입 정보까지 포함하여 종합적으로 정확한 타입 정보를 포함하는 표현식을 재생산한다.

제약 사항들 조합에 대한 조건에 부합하는 경우에만 점수를 주고, 최종적으로 점수가 가장 높은 타입 제약을 선택한다. 같은 점수인 경우 좀 더 세부적인 타입을 선택한다.

타입 변환

Type Cast

타입 변환은 비슷한 종류의 타입끼리만 타입을 바꾸는 것을 의미한다.

타입의 종류가 같다는 것은, 데이터 타입의 메모리 구조가 동일하고 다루는 소재가 다른 타입이라는 것을 의미한다.

  • StringInt는 구조가 다른 타입이므로 타입 변환이 불가능하다.
  • 구조체 타입이나 클래스 타입에서 상속받은 객체들끼리는 구조 동일성이 유지되므로 타입 변환이 가능하다.
  • 숫자를 표시하는 타입들은 구조가 동일하므로 서로 전환이 가능하다.
    • 값에 대한 손실이 발생할 수 있는 경우에는 반드시 명시적으로 타입을 지정해야 한다.

의미 있는 값 vs. 의미 있는 레퍼런스

Value semantics vs. Reference semantics

의미 있는 레퍼런스는, 레퍼런스 방식으로 참조하는 대상이 중요하다는 것을 의미한다.

의미 있는 값은, 값 자체가 중요하다는 것을 의미한다.

Swift는 둘 모두를 지원하지만, ‘의미 있는 값’에 그 비중이 더 쏠려 있다. 함수 중심 프로그래밍에서는 함수에서 다루는 변수가 불변 변수어야 부작용이 없기 때문이다.

값 방식은 참조 계산을 하지 않기 때문에 병렬 처리나 성능 최적화 측면에서 유리하다.

의미 있는 값 의미 있는 레퍼런스
기본 행동 값을 복사해서 사용 레퍼런스에 대한 포인터만 복사
메모리 관리 주로 스택에서 자동 변수로 처리 힙에 있는 레퍼런스 영역을 포인터 변수로 처리
참조 계산 사용하지 않음(자기 자신을 소유) ARC 활용
컬렉션 동작 컬렉션이 값을 그대로 참조하여 불변 상태 유지 컬렉션이 레퍼런스를 참조하고 참조 계산으로 동작
성능 특성 copy-on-write : 복사 이후에 값을 변경하기 전까지는 이전 값을 그대로 사용 병렬 처리에 제한적이고 최적화 한계가 있음
함수 중심 프로그래밍 불변 값을 다루므로 함수 중심 개발에 적합 레퍼런스 내용이 바뀌거나 상태가 바뀌는 부작용이 있음

Objective-C는 클래스 객체를 사용하는 경우 의미 있는 레퍼런스 방식을 취하고, C 언어와 호환하기 위한 내장 타입의 경우 의미 있는 값 방식을 취한다.

Swift에서 기본적인 타입으로 의미 있는 값 방식을 취하는 타입을 사용하는 것은 개발자에게 편리할 뿐만 아니라 프로그래밍 언어의 완성도를 높여주는 요소가 된다.

타입별 성능 비교

의미 있는 값 방식을 취하는 대표적인 타입인 구조체 / 의미 있는 레퍼런스 방식을 취하는 대표적인 타입인 클래스, 프로토콜 타입에 대한 메모리 공간, 참조 계산, 메소드 디스패치 동작을 비교해본다.

스택 영역을 사용하는 경우, 사용시 스택 포인터를 증가시키고 사용하지 않을 때 스택 포인터를 감소시키기만 하면 된다.

반면 힙 영역을 사용하는 경우 비어 있는 힙 공간을 찾고 빈 메모리 공간을 처리하기 위한 별도의 데이터 구조가 필요하다.

스레드 안전 동작이 필요한 경우 힙이 스택보다 상대적으로 느리다.

프로토콜 타입의 경우 3워드보다 작은 크기의 값은 스택만 사용하지만, 그것보다 큰 크기의 값은 힙 공간을 추가적으로 사용하므로 상대적으로 느려질 수 있다.

참조 계산의 경우 참조 개수를 증감시키기 위한 동작이 필요하다. 또한 여러 스레드에서 참조 계산에 접근하도록 처리하기 위한 부가적인 처리를 필요로 한다.

메모리 공간 참조 계산 메소드 디스패치
구조체 타입 스택 사용(빠름) 값만 있는 경우 사용하지 않음. 클래스를 많이 포함할수록 느려짐 정적 디스패치(빠름)
클래스 타입 힙 사용(느림) 사용(느림) 동적 디스패치(느림)
파이널 클래스 타입 힙 사용(느림) 사용(느림) 정적 디스패치(빠름)
프로토콜 타입의 작은 크기 값 스택 사용(빠름) 사용하지 않음(빠름) PWT 기반 동적 디스패치(빠름)
프로토콜 타입의 큰 크기 값 힙 공간 추가 사용(복사하면 더 느려짐) 클래스를 많이 포함할수록 느려짐 PWT 기반 동적 디스패치(빠름)

PWT : Protocol Witness Table

정적 디스패치는 컴파일 시점에 함수의 메모리 주소를 찾아두기 때문에 런타임에서 해당 주소로 바로 이동할 수 있다.

동적 디스패치는 런타임에서 구현 함수 목록에서 함수 메모리 주소를 찾아 이동해야 하므로 상대적으로 느리다. 인라인 처리나 최적화가 불가능하다.

요약

Swift 타입 시스템을 이해하는 것은 타입을 지정하지 않았을 때나, 타입 변환을 해야 하는 경우 반드시 필요하다.

타입에 적합한 메모리 관리 방식에 대해 고민해야 한다.

열거 타입

Swift의 열거 타입은 열거하는 경우에 따라 문자열 타입이나 실수 타입도 지정할 수 있고, 모든 값이 있을 필요도 없고, 모두 다 같은 타입이 아니어도 되며, 함수를 만들 수 있고 확장도 가능하다.

열거 타입과 프로토콜

열거 타입에 정의한 값은 Hashable 프로토콜을 준수해야 한다. 이는 Equatable 프로토콜을 상속받아 만들어졌다.

enum PenModels {
case ballPen
case namePen
}

위와 같은 열거형 정의에 대한 SIL을 살펴보아 다음의 사실을 확인할 수 있다.

  • 내부 case 구문은 hashValue.getter 내부로 들어가 분기 처리된다. integer_literal 형태로 할당된 값은 Swift.Int.hashValue.getter() 함수를 통해서 해시 값으로 바뀌어 리턴된다.
  • 프로토콜 구현 함수라는 것을 인식하기 위해 증거witness 메소드를 별도로 만든다.
  • 프로토콜에 대한 증거 테이블Protocol Witness Table : PWT을 만들어 놓고 런타임에 증거 테이블에서 동적으로 다형성 함수를 찾을 수 있도록 도와준다.

프로토콜 타입과 증거 테이블

클래스 타입에 대한 상속 및 다형성은 가상 함수들을 런타임에서 찾는 동적 디스패치 방식을 사용한다. (V-table)

다른 타입들은 프로토콜 중심 프로그래밍 방식에 맞추어, 프로토콜 증거 테이블을 사용하여 다형성을 구현한다.

변수를 포함하는 프로토콜을 컴파일하면 PWT와 함께 VWT(Value Witness Table)도 함께 만들어 진다. 이는 value semantics를 갖는 타입에 대한 기본 동작을 다루는 생성allocate, 복사copy, 파괴destruct, 해제deallocate 함수들에 대한 참조 테이블이다.

값 크기가 버퍼 크기(3워드)보다 작으면 스택 공간에 그대로 저장되며, 그보다 크면 힙에 데이터 구조를 생성하고 버퍼에는 힙 공간의 주소를 저장한다.

그러므로 프로토콜 타입에서 value semantics를 사용하려면 버퍼보다 작은 데이터 구조를 사용해야 한다.

Equatable 프로토콜

열거형에서 Equatable 프로토콜은 내부적으로 다음과 같이 동작하여 값의 동등함을 비교한다.

  • 좌우에서 열거형 케이스에 대한 파라미터를 받아, 좌측 값에 대한 case 비교문 값과 우측 값에 대한 case 비교문 값을 구한다.
  • 구해진 두 값(Int 타입)에 대해 == 함수를 적용하여 최종적으로 같은 값인지 판단한다.

연관 값을 가지는 열거 타입

enum PatientID {
case socialNumber(String)
case registeredNumber(Int)
}

연관 값은 위와 같은 코드를 작성하여 정의할 수 있다.

이러한 경우 Hashable 또는 Equatable 프로토콜을 구현하는 내부 함수는 만들어지지 않는다. case 구문으로 값이 같은지 비교하지 않더라도 특정 값을 바로 적용할 수 있기 때문이다.

가공 없는 값을 가지는 열거 타입

가공 없는 값 == 원시 값 == Raw Value

enum PenModels: Int {
case ballPen = 0
case namePen = 1
}

원시 값은 위와 같은 코드를 작성하여 정의할 수 있다.

이러한 경우 열거 타입 생성자가 만들어진다. 이 생서앚에 가공 없는 값을 전달하여 열거 타입과 매칭이 되면 값이 들어가고, 매칭이 되지 않으면 null을 할당하므로 Optional<enum.PenModels> 타입을 리턴한다.

스택에 만든 로컬 변수를 비교할 때 == 연산자가 아닌 ~= 연산자를 사용한다.

요약

Swift의 열거 타입은 패턴 매칭과 함께 확장 가능한 막강한 데이터 구조 타입이다.

구조체 타입과 함께 value semantics를 갖는다.

Optional, ImplicitlyUnwrappedOptional, Process, Bit 타입 등이 열거 타입을 기반으로 만들어졌다.

구조체 타입

Swift 표준 라이브러리는 대부분 구조체 타입을 사용하여 만들어졌으며, Swift에서는 클래스보다는 구조체를 사용하여 프로그래밍하는 것이 효율적이다.

구조체 타입

struct Car {
let model = "apple"
}

위 코드에 대한 SIL에서 struct.Car.init(), struct.Car.model.getter 함수가 만들어진다.

  • init() 함수는 구조체를 위한 메모리 박수를 할당한 후, 내부 변수 타입인 String 타입 초기화 함수를 사용하여 초기 값 “apple”을 지정하고, struct_element_addr() 명령으로 model 변수 위치에 저장하고, retain_value() 명령으로 자체 소유권을 지정하고 반환한다.
  • getter() 함수는 struct_extract() 명령으로 Car.model 위치에서 값을 읽어오고, retain_value() 명령으로 자체 소유권을 지정하고 반환한다.

가변 변수가 포함된 경우

struct Car {
var model = "apple"
}

위의 코드에 대하여 struct.Car.init(model:) 이니셜라이저, struct.Car.model.setter() 함수가 추가로 생성된다.

이는 model 변수에 대한 초기 값을 지정하여 객체를 초기화할 수 있게 해준다. (멤버와이즈 이니셜라이저)

SIL 상에서 해당 이니셜라이저는 기존 지정 이니셜라이저(struct.Car.init())을 호출하지 않고, model 초기 값과 함께 struct &Car 명령을 실행하고 반환받은 값을 그대로 리턴한다.

setter()의 경우 두 가지 종류(setter / materializeForSet)가 만들어진다.

  • setter()의 경우 기존 구조체 값을 inout으로 전달받지만 내부적으로 임시로 구조체를 복사하기 위한 박스가 하나 더 만들어진다.
  • materializeForset()의 경우 var 변수에 대한 초기 값을 바로 할당하는 경우가 아닌 경우에 사용된다. 개발자가 직접 호출할 수 없다.

구조체 타입 기반의 스위프트 타입

현재 Swift 표준 라이브러리의 대부분의 타입은 구조체다. Swift 런타임으로 Cocoa 라이브러리의 상당수를 구조체 타입 기반으로 다시 작성했다.

이들은 런타임에 대한 오버헤드를 갖고 있는 Objective-C 기반 바이너리보다 상대적으로 빠르게 동작한다.

요약

Swift의 구조체는 value semantics를 갖는다. 대부분 성능 향상을 위해 스택에 값을 할당하고 사용한다.

구조체 구조가 동적으로 변화하거나 크기가 너무 크다면 힙 공간을 사용하기도 한다.

힙 공간에 있는 구조체나 글로벌 구조체는 함수 범위가 벗어나도 해당 구조체를 참조할 수 있다. 이 경우에도 참조 카운팅은 하지 않으므로 순환 참조 문제가 발생하지 않는다.

문자열 타입

Swift의 문자열 타입(String)은 유니코드를 다루기 적합하도록 구현되었다.

내부적으로 문자열 코어(StringCore) 타입을 포함한다. 복사할 경우 문자열 코어도 복사한다.

copy-on-write 방식을 사용하여 복사하는 것 자체가 메모리를 낭비하지는 않으며, 복사한 문자열을 변경하는 경우 O(N)의 성능 특성을 갖게 된다.

Cocoa 프레임워크의 NSString은 내부에 여러 문자열 요소를 처리하는 클래스를 연결해주는 껍데기 타입이었다. Swift의 String도 내부 타입을 포함하고 프로토콜로 확장된 타입이다.

StringEquatable 준수는 캐노니컬 동등비교canonical equivalence 유니코드 표준 방식을 지원하여, 유니코드 문자끼리 다른 코드 값을 가지고 있더라도 그 언어적 의미와 외형이 동일하면 동등하다고 판단한다.

(초성 ㄱ, 중성 ㅏ, 종성 ㄱ == 각)

문자열을 비교할 때, core 변수의 문자열이 ASCII 문자열인지, UTF8인지, UTF16인지, NSString인지에 따라 각기 다른 알고리즘을 적용하여 비교한다.

  • ASCII 문자열인 경우 swift_stdlib_memcmp() 함수로 메모리 비교를 통해 비교한다.
  • 문자열 타입인 경우 NSString 객체 포인터를 가지고 비교한다.

문자열 코어

StringCore는 ASCII 형태부터 UTF16 형태까지 모두 저장할 수 있는 최적화된 내부 문자열 타입이다.

  • 기본적으로 문자열 저장소에 대한 포인터(baseAddress), 문자열 길이나 옵션(countAndFlags), 문자열 소유 객체(owner)를 프로퍼티로 갖는다.
  • 문자열 저장소를 다루는 기본 코드를 포함하고 있다.
    • 문자열 추가 및 변경 시에 기존 공간이 가득 차면, 기존 저장소보다 더 큰 저장소를 할당하고 문자열을 옮기는 작업을 한다. 이 때 의도적으로 기존 저장소보다 2배 이상 크게 만든다.

문자열과 메모리

copy-on-write 방식으로 동작하여, 문자열 메모리를 복사한 이후에 문자열 값이 바뀌기 전까지는 동일한 메모리를 참조하고, 실제로 값이 바뀌는 순간에 새로운 메모리를 할당한다.

문자열 뷰

문자열 타입은 문자의 배열, 문자들을 모아놓은 컬렉션 타입이 아니며, 문자열 타입을 마치 컬렉션 타입처럼 다룰 수 있도록 도와주는 뷰 속성들이 있을 뿐이다.

문자열 뷰

CharacterView

extended grapheme cluster라는 유니코드 표준 방식을 지원한다. characters 프로퍼티를 통해 접근 가능하다.

이 때 내부적으로는 CharacterView라는 새로운 구조체를 만들고, 문자열 코어를 복사하여 문자 단위로 접근할 수 있도록 확장한 함수들을 제공한다.

유니코드 스칼라 뷰

UnicodeScalarView

유니코드 표준에서 사용하는 21비트 코드로 구성된 UnicodeScalar 값에 접근할 수 있도록 도와준다. unicodeScalars 프로퍼티를 통해 접근 가능하다.

내부적으로 UnicodeScalar 구조체 내부에 새로운 문자열 코어를 복사하여 접근 가능하도록 도와준다.

UTF8View와 UTF16View

유니코드 표준에서 8비트 코드 유닛을 지원하는 UTF8 문자, 16비트 코드 유닛을 지원하는 UTF16 문자에 대한 컬렉션 뷰를 제공한다.

요약

Swift의 문자열은 NSString과의 호환성을 유지하지만 동일한 구현체는 아니다. 유니코드 기반의 value semantics를 갖는 타입이다.

문자열 뷰 형태로 컬렉션 함수를 제공하여 메모리 효율성, 편리함, 성능 특성의 적절한 타협점을 제공한다.

Swift의 리터럴 문자열도 메모리의 TEXT 섹션에 생성된다.

부가적인 타입들

안전하지 않은 포인터

UnsafePointerUnsafeMutablePointer는 메모리 주소를 다루기 위해 구조체 타입으로 포장된 제네릭 포인터 타입이다.

자동으로 메모리 관리를 해주지 않으므로 불안전한 포인터 타입으로 메모리 관리를 해주어야 한다.

OpaquePointer의 경우 Swift 포인터 타입으로 다룰 수 없는 C 언어 포인터와의 호환성을 유지하기 위해 사용된다.

옵셔널

public enum Optional<Wrapped>: ExpressibleByNilLiteral {
case none
case some(Wrapped)
}
  • map() 함수는 넘겨받은 함수의 결과가 nil이면 옵셔널로 감싸서 리턴한다. 옵셔널 값을 반환하지 않는 클로저를 넘겨 사용한다.
  • flatMap() 함수는 map() 함수와 비슷하게 동작하지만 옵셔널로 감싸 리턴하는 동작이 없다. 옵셔널 값을 반환하는 클로저를 넘겨 사용한다.

위처럼 Optional 타입은 열거 타입으로 none 케이스와 some(Wrapped) 케이스를 갖는다.

옵셔널 강제 제거

var unknownString: String!
var unknownString2: ImplicitlyUnwrappedOptional<String>

ImplicitlyUnwrappedOptional은 Swift 4.2에서 사라졌다. (SE-0054)

이는 옵셔널 타입과 동일하게 처리되나, 강제 제거 타입이라는 것을 알고 있으면서 옵셔널을 제거하고 값에 접근할 수 있게 도와준다.

슬라이스

Slice. 컬렉션 타입의 내부 요소들 일부에 접근하는 뷰를 제공하는 구조체 타입.

컬렉션 저장소에서 참조하는 시작 지점과 끝 지점을 제공하는 것으로 구현되어 있다.

컬렉션 데이터 요소를 복사해서 별도로 저장하는 것이 아니므로 생성하는 것 자체에 O(1)의 복잡도를 갖게 된며, 메모리 상의 같은 주소를 가리킨다.

시퀀스

Sequence. for-in 반복문에서 내부 요소에 순차적으로 접근하기 위한 프로토콜 타입.

반복문에서 컨테이너를 탐색하는 반복 패턴Iterator Pattern을 구현하는 타입이다.

IteratorProtocol

Sequence 타입에서 반복자를 구현하기 위해 해당 프로토콜을 사용한다.

  • makeIterator() 함수를 구현하여 이터레이터를 생성한다.
  • 위의 값이 nil이 될 때까지 next() 함수를 반복 호출하여 탐색한다.

시퀀스 타입 성능 특성

복잡도는 O(1)이어야 한다.

AnyObject

모든 클래스 타입을 지칭하는 프로토콜 타입이다. 모든 클래스는 해당 프로토콜을 암시적으로 준수하고 있다.

Objective-C 객체와의 연결을 위해 사용된다.

요약

Swift 표준 라이브러리는 Cocoa 프레임워크와의 연결성을 고려하여 만들어졌다.

Swift 표준 라이브러리는 여전히 발전하고 있다.

스위프트 런타임

Swift 런타임 또한 Objective-C 런타임과 비슷하게 메모리 관리 기능, 프로토콜 타입이나 제네릭 타입에 대한 타입 제약을 확인하는 런타임 타입 시스템을 포함하고 있다.

메모리 관리

힙 메모리에 생성되는 Swift 타입에 사용하는 API를 구현한다.

  • Object는 힙 메모리에 생성하는 내부 HeapObject 구조체를 의미한다.
  • Box는 힙 객체와 객체 내부 값을 묶은 튜플 형태이며, ExistentialContainer에서도 활용한다.

참조 계산용 메모리 관리

Swift 내부 타입이 사용하는 힙 객체 참조에 대한 설정, 초기화, 복사, 소멸을 관리하는 API를 제공한다.

  • Objective-C로 연결한 객체에 대한 참조 계산
  • Swift 타입이 사용하는 힙 객체에 대한 참조 계산
  • 타입을 모르는 소유한 객체에 대한 참조 계산
  • 타입을 모르는 소유하지 않은unowned 참조에 대한 설정, 초기화, 복사, 소멸 관리
  • 타입을 모르는 약한 참조weak에 대한 설정, 초기화, 복사, 소멸 관리
  • 소유하지 않은unowned 특정 힙 객체에 대한 참조 계산
  • 약한 참조에 대한 설정, 초기화, 복사, 소멸 관리
  • 포인터가 특정 객체를 참조했는지 여부 확인
  • 힙 객체를 메모리에서 해제

동적 타입 변환

런타임에서 타입 정보를 이용하여 동적으로 타입을 변환하기 위한 API를 제공한다.

Objective-C 연결

런타임에서 Objective-C 객체와 연결하거나 연결 가능성을 판단하기 위한 API를 제공한다.

메타 타입

런타임에서 타입에 대한 메타데이터를 다루기 위한 API를 제공한다.

요약

Swift도 Objective-C와 마찬가지로 런타임에서 거의 동등한 기능을 제공하기 위한 런타임 API를 제공한다.

Swift 표준 라이브러리 및 핵심 타입들은 런타임 API를 활용하여 만들어진다.

런타임 함수의 종류와 역할을 이해하여 특정 타입을 저수준에서 제어할 수 있다.

스위프트 파운데이션

기존 Foundation 프레임워크를 Swift를 사용하여 다시 작성한 것들이다.

파운데이션 타입

기존 NS 계열 클래스를 포장하여 만들거나, 구조체 타입에 새롭게 만든 내부 클래스로 이루어져 있다.

요약

Swift 3에서 기존 플랫폼에 의존적이었던 프레임워크 기반 코드를 다른 플랫폼에서도 독립적으로 사용할 수 있도록 개선한 사항들이 많이 있다.

네이티브 구조체를 사용하여 Foundation 프레임워크를 구성하는 사례가 많아지고 있다.