name
이라는 저장된 상수 프로퍼티를 정의하는 Person
이라는 간단한 클래스로 시작합니다:Person
클래스는 인스턴스의 name
프로퍼티에 설정하고 초기화가 진행 중임을 나타내는 메세지를 출력하는 초기화 구문을 가집니다. Person
클래스는 클래스의 인스턴스가 할당 해제될 때 메시지를 출력하는 초기화 해제 구문도 가지고 있습니다.Person
인스턴스에 여러개 참조를 설정하기 위해 사용되는 Person?
타입의 3개의 변수를 정의합니다. 이 변수는 Person
이 아닌 Person?
인 옵셔널 타입 이므로 nil
의 값으로 자동으로 초기화 되고 현재는 Person
인스턴스를 참조하지 않습니다.Person
인스턴스를 생성하고 이 3개의 변수중 하나에 할당합니다:"John Appleseed is being initialized"
메세지는 Person
클래스의 초기화 구문을 호출할 때 출력됩니다. 이것은 초기화가 발생했음을 확인합니다.Person
인스턴스는 reference1
변수에 할당되기 때문에 reference1
에서 새로운 Person
인스턴스에 대한 강한 참조가 있습니다. 하나의 강한 참조가 있기 때문에 ARC는 이 Person
을 메모리에 유지하고 할당 해제하지 않습니다.Person
인스턴스를 2개 이상의 변수에 할당하면 해당 인스턴스에 대한 2개 이상의 강한 참조가 설정됩니다:Person
인스턴스에 3개 의 강한 참조가 있습니다.nil
을 할당하여 강한 참조 중 2개를 중단하면 하나의 강한 참조만 남고 Person
인스턴스는 할당 해제되지 않습니다:Person
인스턴스를 할당 해제하지 않습니다. 이 때부터 Person
인스턴스를 더이상 사용하지 않는 것이 명백합니다:Person
인스턴스를 생성하고 더이상 필요치 않을 때 Person
인스턴스를 할당 해제하기 위해 참조의 수를 추적할 수 있습니다.Person
과 Apartment
라는 2개의 클래스를 정의합니다:Person
인스턴스는 String
타입의 name
프로퍼티와 초기값이 nil
인 옵셔널 apartment
프로퍼티를 가지고 있습니다. 사람이 항상 아파트를 가지고 있지 않기 때문에 apartment
프로퍼티는 옵셔널입니다.Apartment
인스턴스는 String
타입의 unit
프로퍼티와 초기값이 nil
인 옵셔널 tenant
프로퍼티를 가지고 있습니다. 아파트를 항상 보유하는 것은 아니므로 tenant
프로퍼티는 옵셔널입니다.Person
과 Apartment
의 인스턴스가 예상되로 초기화 해제되었는지 확인할 수 있습니다.Apartment
와 Person
인스턴스를 설정할 john
과 unit4A
라는 옵셔널 타입의 2개의 변수를 정의합니다. 이 변수 모두 옵셔널 이므로 초기값으로 nil
을 가집니다:Person
인스턴스와 Apartment
인스턴스를 생성할 수 있고 이 새로운 인스턴스를 john
과 unit4A
변수에 할당할 수 있습니다:john
변수는 이제 새로운 Person
인스턴스에 대한 강한 참조를 가지고 있고 unit4A
변수는 새로운 Apartment
인스턴스에 대한 강한 참조를 가지고 있습니다:!
)는 john
과 unit4A
옵셔널 변수 내에 저장된 인스턴스를 언래핑 하고 접근하기 위해 사용되므로 해당 인스턴스의 프로퍼티를 설정할 수 있습니다:Person
인스턴스는 이제 Apartment
인스턴스에 대한 강한 참조를 가지고 Apartment
인스턴스는 Person
인스턴스에 대한 강한 참조를 가집니다. 따라서 john
과 unit4A
변수에 의해 가진 강한 참조를 중단할 때 참조 카운트는 0으로 떨어지지 않고 인스턴스는 ARC에 의해 할당 해제되지 않습니다:nil
로 설정할 때 초기화 구문은 호출되지 않습니다. 강한 참조 사이클은 Person
과 Apartment
인스턴스가 할당 해제되는 것을 방지하여 앱에서 메모리 누수를 유발합니다.john
과 unit4A
변수를 nil
로 설정한 후에 강한 참조를 나타냅니다:Person
과 Apartment
인스턴스 간의 강한 참조는 남아있고 중단될 수 없습니다.Apartment
예제에서 아파트는 어느 시점에 소유자가 없을 수 있는 것이 적절하므로 이러한 경우 약한 참조는 참조 사이클을 끊는 적절한 방법입니다. 반대로 다른 인스턴스의 수명이 동일하거나 더 긴 경우 미소유 참조를 사용합니다.weak
키워드를 위치시켜 약한 참조를 나타냅니다.nil
로 약한 참조를 자동으로 설정합니다. 그리고 약한 참조는 런타임에 값을 nil
로 변경하는 것을 허락해야 하므로 항상 옵셔널 타입의 상수가 아닌 변수로 선언됩니다.NOTE 프로퍼티 관찰자는 ARC가 약한 참조를nil
로 설정할 때 호출되지 않습니다.
Person
과 Apartment
예제와 동일하지만 한가지 중요한 차이점이 있습니다. 이번에는 Apartment
타입의 tenant
프로퍼티는 약한 참조로 선언됩니다:john
과 unit4A
)에서의 강한 참조와 두 인스턴스 간의 연결은 이전과 같이 생성됩니다:Person
인스턴스는 Apartment
인스턴스에 대해 아직 강한 참조를 가지고 있지만 Apartment
인스턴스는 이제 Person
인스턴스에 대해 약한 참조를 가지고 있습니다. 이것은 john
변수에 nil
을 설정하여 강한 참조를 끊으면 Person
인스턴스에 대해 더이상 강한 참조가 아님을 의미합니다:Person
인스턴스에 대해 강한 참조를 가지지 않기 때문에 할당 해제되고 tenant
프로퍼티는 nil
로 설정됩니다:Apartment
인스턴스에 대한 유일한 강한 참조는 unit4A
변수에서 가져온 것입니다. 강한 참조를 끊으면 Apartment
인스턴스에 대한 강한 참조는 더이상 없습니다:Apartment
인스턴스에 대한 강한 참조가 더이상 없으므로 할당 해제됩니다:NOTE 가비지 콜렉션을 사용하는 시스템에서는 메모리 압력이 가비지 콜렉션을 트리거 할 때만 강한 참조가 없는 객체가 할당 해제되기 때문에 간단한 캐싱 메커니즘을 구현하는데 약한 포인터가 사용되는 경우가 있습니다. 그러나 ARC를 사용하면 마지막 강한 참조가 제거되자마자 값이 할당 해제되어 약한 참조는 이러한 목적에 적합하지 않습니다.
unowned
키워드를 위치시켜 미소유 참조를 나타냅니다.nil
로 설정하지 않습니다.IMPORTANT 참조가 항상 할당 해제되지 않은 인스턴스를 참조한다고 확신하는 경우에만 미소유 참조를 사용합니다.인스턴스가 할당 해제된 후에 미소유 참조의 값에 접근하려고 하면 런타임 에러가 발생합니다.
Customer
와 CreditCard
인 2개의 클래스를 정의합니다. 이 두 클래스는 프로퍼티로 다른 클래스의 인스턴스를 각각 저장합니다. 이 관계는 강한 참조 사이클을 생성할 가능성이 있습니다.Customer
와 CreditCard
간의 관계는 위의 약한 참조 예제에서 본 Apartment
와 Person
간의 관계와 약간 다릅니다. 이 데이터 모델은 고객은 신용카드를 가지고 있거나 가지고 있지 않을 수 있지만 신용카드는 항상 고객과 연관되어 있습니다. CreditCard
인스턴스는 참조하는 Customer
보다 오래 지속되지 않습니다. 이것을 표현하기 위해 Customer
클래스는 옵셔널 card
프로퍼티를 가지지만 CreditCard
클래스는 미소유와 옵셔널이 아닌 customer
프로퍼티를 가집니다.CreditCard
인스턴스는 사용자 정의 CreditCard
초기화 구문에 number
값과 customer
인스턴스를 전달해서만 생성될 수 있습니다. 이렇게 하면 CreditCard
인스턴스가 생성될 때 CreditCard
인스턴스에 항상 연관된 customer
인스턴스를 가지고 있습니다.customer
프로퍼티에 미소유 참조로 정의합니다:NOTECreditCard
클래스의number
프로퍼티는Int
가 아닌UInt64
타입으로 정의되어number
프로퍼티의 용량이 32 비트와 64 비트 시스템 모두에 16 자리 카드번호를 저장할 수 있을만큼 충분히 크도록 합니다.
john
이라는 옵셔널 Customer
변수를 정의합니다. 이 변수는 옵셔널이므로 nil
의 초기값을 가집니다:Customer
인스턴스를 생성할 수 있고 해당 고객의 card
프로퍼티로 새로운 CreditCard
인스턴스를 초기화 하고 할당하기 위해 사용할 수 있습니다:Customer
인스턴스는 이제 CreditCard
인스턴스에 대한 강한 참조를 가지고 있고 CreditCard
인스턴스는 Customer
인스턴스에 대해 미소유 참조를 가지고 있습니다.john
변수에 의해 강한 참조를 끊을 때 미소유 customer
참조 때문에 Customer
인스턴스에 대해 더이상 강한 참조를 가지지 않습니다:Customer
인스턴스에 대해 강한 참조가 아니므로 할당 해제됩니다. 후에 CreditCard
인스턴스에 대해 더이상 강한 참조가 아니므로 이것도 할당 해제됩니다:john
변수를 nil
로 설정한 후에 Customer
인스턴스와 CreditCard
인스턴스 모두 "deinitialized" 메서지를 출력하는 초기화 해제 구문을 보여줍니다.NOTE 위의 예제는 안전한 미소유 참조를 어떻게 사용해야 하는지 보여줍니다. Swift는 성능상의 이유 등으로 런타임 안전 검사를 비활성화 하는 경우에 대해 안전하지 않은 미소유 참조 (unsafe unowned references)를 제공합니다. 모든 안전하지 않은 작업과 마찬가지로 해당 코드의 안정성을 검사하는 것에 책임을 져야 합니다.unowned(unsafe)
로 작성하여 안전하지 않은 미소유 참조를 나타냅니다. 참조하는 인스턴스가 할당 해제된 후에 안전하지 않은 미소유 참조에 접근하려고 하면 프로그램은 안전하지 않은 작업으로 인스턴스가 사용한 메모리 위치에 접근하려고 합니다.
nil
로 설정되어 있는지 확인해야 합니다.Department
는 과에서 제공하는 각 과정에 강한 참조를 유지합니다. ARC 소유권 모델에서 과는 과정을 소유하고 있습니다. Course
는 과에 대한 것과 객체를 소유하지 않은 학생이 수강해야 하는 다음 과정에 대한 2개의 미소유 참조를 가지고 있습니다. 모든 과정은 과의 부분이므로 department
프로퍼티는 옵셔널이 아닙니다. 그러나 일부 과정은 후속 과정이 없기 때문에 nextCourse
프로퍼티는 옵셔널 입니다.nextCourse
프로퍼티에 저장된 다음 과정을 제안하며 이는 학생이 과정을 완료한 후 수강해야 하는 과정에 대한 미소유 옵셔널 참조를 유지합니다.nil
이 될 수 있다는 점을 제외하고 미소유 참조는 ARC에서 수행하는 것과 동일하게 동작합니다.nextCourse
가 항상 할당 해제되지 않은 과정을 참조하도록 해야합니다. 예를 들어 department.courses
에서 과정을 삭제할 때 다른 과정에 있을 수 있는 모든 참조를 삭제해야 합니다.NOTE 옵셔널 값의 기본 타입은 Swift 표준 라이브러리에 열거형 인Optional
입니다. 그러나 옵셔널은 값 타입에unowned
를 표기할 수 없는 규칙에 대해 예외입니다.클래스를 래핑한 옵셔널은 참조 카운팅을 사용하지 않으므로 강한 참조를 옵셔널로 유지할 필요가 없습니다.
Person
과 Apartment
예제는 둘 다 nil
이 될 수 있는 프로퍼티가 강한 참조 사이클을 유발할 수 있는 가능성이 있는 상황을 보여줍니다. 이 시나리오는 약한 참조로 해결하는 것이 가장 좋습니다.Customer
와 CreditCard
예제는 nil
이 허용되는 하나의 프로퍼티와 nil
일 수 없는 프로퍼티가 강한 참조 사이클을 유발할 수 있는 가능성이 있는 상황을 보여줍니다. 이 시나리오는 미소유 참조로 해결하는 것이 가장 좋습니다.nil
이 되어서는 안되는 세번째 시나리오가 있습니다. 이 시나리오에서는 한 클래스의 미소유 프로퍼티를 다른 클래스에 암시적으로 언래핑된 옵셔널 프로퍼티와 결합하는 것이 유용합니다.Country
와 City
인 2개의 클래스를 정의합니다. 이 데이터 모델에서 모든 국가는 항상 수도를 가지고 있고 모든 도시는 항상 국가에 속해 있어야 합니다. 이것을 표현하기 위해 Country
클래스는 capitalCity
프로퍼티를 가지고 있고 City
클래스는 country
프로퍼티를 가지고 있습니다:City
에 대한 초기화 구문은 Country
인스턴스를 가지고 있고 country
프로퍼티에 저장합니다.City
에 대한 초기화 구문은 Country
에 대한 초기화 구문 내에서 호출됩니다. 그러나 Country
에 대한 초기화 구문은 2단계 초기화 (Two-Phase Initialization) 에서 설명 했듯이 새로운 Country
인스턴스가 완벽히 초기화 될 때까지 City
초기화 구문에 self
를 전달할 수 없습니다.Country
의 capitalCity
프로퍼티를 타입 설명의 끝에 느낌표 (City!
)로 표시되는 암시적 언래핑된 옵셔널 프로퍼티로 선언합니다. capitalCity
프로퍼티는 다른 옵셔널과 같이 nil
의 기본값을 가지지만 암시적 언래핑된 옵셔널 (Implicitly Unwrapped Optionals) 에서 설명 했듯이 언래핑 할 필요없이 값에 접근할 수 있다는 의미입니다.capitalCity
는 기본 nil
값을 가지므로 새로운 Country
인스턴스는 Country
인스턴스가 초기화 구문 내에서 name
프로퍼티를 설정하는 즉시 새로운 Country
인스턴스는 완벽히 초기화 된 것으로 간주합니다. 이것은 Country
초기화 구문은 name
프로퍼티가 설정되는 즉시 암시적 self
프로퍼티를 참조하고 전달할 수 있다는 의미입니다. 따라서 Country
초기화 구문은 capitalCity
프로퍼티를 설정할 때 City
초기화 구문에 대한 하나의 파라미터로 self
를 전달할 수 있습니다.Country
와 City
인스턴스를 생성하고 capitalCity
프로퍼티는 옵셔널 값을 언래핑 하기위해 느낌표를 사용할 필요없이 직접 접근할 수 있다는 의미입니다:capitalCity
프로퍼티는 초기화가 완료되면 옵셔널이 아닌 값처럼 사용되고 접근할 수 있지만 강한 참조 사이클은 피할 수 있습니다.self.someProperty
와 같이 인스턴스의 프로퍼티에 접근하거나 클로저는 self.someMethod()
와 같이 인스턴스의 메서드를 호출하기 때문에 발생할 수 있습니다. 두 경우 모두 이러한 접근은 클로저가 self
를 "캡처" 하여 강한 참조 사이클을 생성합니다.self
를 참조하는 클로저를 사용할 때 강한 참조 사이클이 어떻게 생성될 수 있는지 보여줍니다. 이 예제는 HTML 문서 내에 개별 요소에 대한 간단한 모델을 제공하는 HTMLElement
라는 클래스를 정의합니다:HTMLElement
클래스는 제목 요소에 대한 "h1"
, 단락 요소에 대한 "p"
, 또는 개행 요소에 대한 "br"
과 같이 요소의 이름을 나타내는 name
프로퍼티를 정의합니다. HTMLElement
는 HTML 요소 내에 렌더링 될 텍스트를 나타내는 문자열을 설정할 수 있는 옵셔널 text
프로퍼티도 정의합니다.HTMLElement
클래스는 asHTML
이라는 지연 프로퍼티 (lazy property)를 정의합니다. 이 프로퍼티는 name
과 text
를 HTML 문자열 조각으로 결합하는 클로저를 참조합니다. asHTML
프로퍼티는 () -> String
또는 "파라미터가 없고 String
값을 반환하는 함수" 타입입니다.asHTML
프로퍼티는 HTML 태그의 문자열 표현을 반환하는 클로저가 할당됩니다. 이 태그는 존재하면 옵셔널 text
값을 포함하고 text
가 존재하지 않으면 텍스트 콘텐츠는 없습니다. 단락 요소에 대해 클로저는 text
프로퍼티가 "some text"
또는 nil
인지에 따라 "<p>some text</p>"
또는 "<p />"
을 반환합니다.asHTML
프로퍼티는 인스턴스 메서드와 비슷하게 이름이 지어지고 사용됩니다. 그러나 asHTML
은 인스턴스 메서드가 아니라 클로저 프로퍼티 이기 때문에 특정 HTML 요소에 대해 HTML 렌더링을 변경하기 원하면 사용자 정의 클로저로 asHTML
프로퍼티의 기본값을 대체할 수 있습니다.asHTML
프로퍼티는 빈 HTML 태그를 반환하는 표현을 방지하기 위해 text
프로퍼티가 nil
이면 일부 텍스트를 기본값으로 하는 클로저로 설정할 수 있습니다:NOTEasHTML
프로퍼티는 요소가 실제로 일부 HTML 출력 타겟에 대한 문자열 값으로 렌더링 되어야 하는 경우에만 필요하므로 지연 프로퍼티로 선언됩니다.asHTML
이 지연 프로퍼티라는 사실은 초기화가 완료되고self
가 존재할 때까지 접근할 수 없으므로 기본 클로저 내에서self
를 참조할 수 있다는 의미입니다.
HTMLElement
클래스는 name
인자와 필요하면 text
인자를 사용하여 새로운 요소를 초기화 하는 단일 초기화 구문을 제공합니다. 이 클래스는 HTMLElement
인스턴스가 할당 해제될 때 메세지를 출력하는 초기화 해제 구문도 정의합니다.HTMLElement
클래스가 새로운 인스턴스를 생성하고 출력하기 위해 사용되는지 보여줍니다:NOTE 위의paragraph
변수는 강한 참조 사이클의 존재를 보여주기 위해 아래에서nil
로 설정할 수 있으므로 옵셔널HTMLElement
로 정의됩니다.
HTMLElement
클래스는 HTMLElement
인스턴스와 기본 asHTML
값으로 사용된 클로저 간에 강한 참조 사이클을 생성합니다. 사이클은 다음과 같습니다:asHTML
프로퍼티는 클로저에 대해 강한 참조를 유지합니다. 그러나 클로저는 바디 내에서 self.name
과 self.text
를 참조하는 방법 처럼 self
를 참조하기 때문에 클로저는 HTMLElement
인스턴스에 다시 강한 참조를 유지한다는 의미로 self
를 캡처 합니다. 둘 사이에 강한 참조 사이클이 생성됩니다 (자세한 내용은 캡처값 (Capturing Values) 을 참고 바랍니다).NOTE 클로저는 여러번self
를 참조하지만HTMLElement
인스턴스에 대한 하나의 강한 참조만 캡처합니다.
paragraph
변수를 nil
로 설정하고 HTMLElement
인스턴스에 대한 강한 참조를 끊으면 강한 참조 사이클 때문에 HTMLElement
인스턴스와 해당 클로저는 할당 해제되지 않습니다:HTMLElement
초기화 해제 구문에 메세지는 출력되지 않으며 HTMLElement
인스턴스는 할당 해제 되지 않음을 보여줍니다.NOTE Swift는 클로저 내에서self
의 멤버를 참조할 때마다someProperty
또는someMethod()
가 아닌self.someProperty
또는self.someMethod()
로 작성해야 합니다. 이것은 실수로self
를 캡첩할 수 있는 것을 기억하는데 도움이 됩니다.
self
처럼 클래스 인스턴스 또는 delegate = self.delegate
와 같은 어떤 값을 초기화된 변수에 대한 참조가 있는 weak
또는 unowned
키워드와 쌍을 이룹니다. 이 쌍은 콤마로 구분하여 대괄호 내에 작성됩니다.in
키워드가 옵니다:nil
이 될 때 약한 참조로 캡처를 정의합니다. 약한 참조는 항상 옵셔널 타입이고 참조하는 인스턴스가 할당 해제되면 자동으로 nil
이 됩니다. 이를 사용하여 클로저의 바디 내에서 존재하는지 확인할 수 있습니다.NOTE 캡처된 참조가nil
이 되지 않으면 약한 참조보다 미소유 참조로 항상 캡처되어야 합니다.
HTMLElement
예제에서 강한 참조 사이클을 해결하기 위해 사용할 적절한 캡처 방법입니다. 다음은 사이클을 피하기 위해 HTMLElement
클래스를 어떻게 작성하는지 보여줍니다:HTMLElement
의 구현은 asHTML
클로저 내에서 캡처 목록의 추가를 제외하면 이전 구현과 동일합니다. 이러한 경우에 캡처 목록은 "강한 참조가 아닌 미소유 참조로 self
를 캡처합니다" 라는 의미의 [unowned self]
입니다.HTMLElement
인스턴스를 생성하고 출력할 수 있습니다:self
의 캡처는 미소유 참조이고 캡처한 HTMLElement
인스턴스를 강하게 유지하지 않습니다. paragraph
변수를 nil
로 강한 참조를 설정하면 아래 예제에서 초기화 해제 구문 메세지를 출력하는 것을 확인했듯이 HTMLElement
인스턴스는 할당 해제 됩니다.