//Person 클래스는 인스턴스 name property에 설정을 초기화가 진행 중임을 나타내는 메시지를 출력구문
class Person {
let name: String
init(name: String) {
self.name = name
print("\(self.name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
//옵셔널 타입 이므로 nil의 값으로 자동으로 초기화 되고 현재는 Person 인스턴스 참조하지 않음
var reference1: Person?
var reference2: Person?
var reference3: Person?
//rc = 1
//Person클래스 초기화 구문을 호출할 떄 출력
//강한 잠조 발생
reference1 = Person(name: "mimi") //mimi is being initialized
//rc = 2
//하나만 해제된다고 해서 해제되지 않음
reference2 = reference1
//rc = 3
reference3 = reference1
//nil을 할당하지만 reference count = 2가 됨
//강한 참조 떄문임
reference1 = nil
//고로 반드시 모든걸 해제하여야 deinit이 호출됨
reference2 = nil
reference3 = nil
//mimi is being deinitialized
Person 인스턴스를 생성하고 더이상 필요하지 않을 떄 할당 해제하기 위해 참조의 수를 추적할 수 있음
클래스 인스턴스 사이의 강한 참조 사이클 (Strong Reference Cycle Between Class Instance)
두 클래스 인스턴스가 서로에 대한 강한 참조를 유지하여 각 인스턴스가 다른 인스턴스를 유지하는 경우 발생 == 강한 참조 사이클
해결방법 약한 참조 / 미소유 참조로 정의하면 강한 참조 사이클은 해결됨
//두 클래스 인스턴스가 서로에 대한 강한 참조를 유지하는 경우
//이 클래스 간을 약한 또는 미소유 참조를 사용하면 해결된다
//1. 강한참조 사이클 발생
class Person1 {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
var apartment: Apartment?
deinit { print("\(name) is being deinitialized")}
}
class Apartment {
let unit: String
init(unit: String) {
self.unit = unit
print("\(self.unit) is being initialized")
}
var tennat: Person1?
deinit { print("Apartment \(unit) is being deinitalized")}
}
var mimi: Person1?
var unit4A: Apartment?
//mimi rc = 1
mimi = Person1(name: "mimi1") //mimi1 is being initialized
//unit4A rc = 1
unit4A = Apartment(unit: "4A") //4A is being initialized
//이렇게만 할당하고 해제를 하게되면
//강한 참조가 되지 않아서 해제가 됨
/**
mimi = nil
unit4A = nil
Apartment 4A is being deinitalized
mimi1 is being deinitialized
**/
//서로 향하도록 설정
//rc가 2가 되면서 할당 해제되지 않음
//mimi rc = 2
mimi!.apartment = unit4A
//unit4A rc = 2
unit4A!.tennat = mimi
//해제 불가
//mini rc = 1
mimi = nil
//unit4A rc = 1
unit4A = nil
john!.apartment = unit4A
unit4A!.tenant = john
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john = nil
unit4A = nil
클래스 인스턴스 간의 강한 참조 사이클 해결 (Resolving Strong Reference Cycles Between Class Instances)
강한 참조 사이클 해결하기 위해 2가지 방법이 있다
약한 참조(weak references)
미소유 참조(unowned references)
약한 참조 weak references
참조하는 인스턴스를 강하게 유지하지 않는 참조
프로퍼티 또는 변수 선언 전에 weak 키워드 위치시켜 약한 참조 나타냄
강하게 유지하지 않기 땜누에 인스턴스가 할당 해제될 수 있음
약한 참조는 런타임에 값을 nil로 변경하는 것을 가능하게 해야하기 때문에 항상 옵셔널타입이 상수가 아닌 변수로 선언해야함
/*=====================
클래스 인스턴스 간의 강한 참조 사이클 해결
(Resolving Strong Reference Cycles Between Class Instances)
======================*/
//약한 참조와 미소유 참조를 통해서 강한참조를 해결할 수 있다
/*=====================
약한 참조
(Weak References)
======================*/
class Person2{
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
var apartment: Apartment1?
deinit { print("\(name) is being deinitialized")}
}
class Apartment1 {
let unit: String
init(unit: String) {
self.unit = unit
print("\(self.unit) is being initialized")
}
weak var tennat: Person2?
deinit { print("Apartment \(unit) is being deinitalized")}
}
var mimi2: Person2?
var unit4A1: Apartment1?
mimi2 = Person2(name: "mimi2")
unit4A1 = Apartment1(unit: "4A1")
mimi2!.apartment = unit4A1
unit4A1!.tennat = mimi2
mimi2 = nil //mimi2 is being deinitialized
unit4A1 = nil //Apartment 4A1 is being deinitalized
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
// "John Appleseed is being deinitialized"
unit4A = nil
// "Apartment 4A is being deinitialized"
미소유 참조 unowned references
약한참조와 다르게 미소유 참조는 다른 인스턴스의 수명이 같거나 더 긴 경우에 사용
프로퍼티 또는 변수 선언 전에 unowned 키워드를 위치시켜 미소유 참조를 나타냄
약한 참조와 달리 미소유 참조는 항상 값을 갖도록 한다
미쇼유로 만들어진 값은 옵셔널로 만들어지지 않고 ARC는 미소유 참조 값을 nil로 설정하지 않음
참조가 항상 할당 해제되지 않은 인스턴스를 참조한다고 확신하는 경우에만 미소유 참조를 사용 인스턴스가 할당 해지된 후에 미소유 참조의 값에 접근하려고 하면 런타임 에러가 발생
/*=====================
미소유 참조
(Unowned References)
======================*/
//약한 참조와 다르게 미소유 참조는 다른 인스턴스의 수명이 길거다 더 긴 경우에 사용
//unowned 변수
/*
IMPORTANT
참조가 항상 할당 해제되지 않은 인스턴스를 참조한다고 확신하는 경우에만 미소유 참조를 사용합니다.
인스턴스가 할당 해제된 후에 미소유 참조의 값에 접근하려고 하면 런타임 에러가 발생합니다.
*/
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized")}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized")}
}
var vip: Customer?
vip = Customer(name: "vip")
vip!.card = CreditCard(number: 1234_5678_9012_3456, customer: vip!)
vip = nil
/*
vip is being deinitialized
Card #1234567890123456 is being deinitialized
*/
john = nil
// Prints "John Appleseed is being deinitialized"
// Prints "Card #1234567890123456 is being deinitialized"
미소유 옵셔널 참조 (Unowned Optional References)
클래스를 참조하는 옵셔널을 미소유로 표기할 수 있습니다. 미소유 옵셔널 참조는항상 유효한 객체를 가리키거나 그렇지 않다면 nil을 할당해 주도록 직접 신경을 써야함
아래 배열에서 한 과목을 제거하고 싶다면 해당 인스턴스의 nextCourse참조를 제거 (그렇지 않으면 참조하려고 할 때 오류가 발생)
참조
- 미소유 옵셔널 참조는 옵셔널의 값인 클래스 인스턴스를 강한참조 하지 않기 때문에, 옵셔널 클래스의 인스턴스가 메모리에서 해제될때 ARC에 의해 보호받지 못함 ( 미소유 참조랑 동일한 동작을 하지만 미소유 옵셔널 참조는 nil이 될수 있음)
/*=====================
미소유 옵셔널 참조
(Unowned Optional References)
======================*/
class Department {
var name: String
var courses: [Course]
init(name: String) {
self.name = name
self.courses = []
}
deinit { print("department \(name) is being deinitialized")}
}
class Course {
var name: String
unowned var department: Department
unowned var nextCourse: Course?
init(name: String, in department: Department) {
self.name = name
self.department = department
self.nextCourse = nil
}
deinit { print("\(name) is being deinitialized")}
}
var department = Department(name: "Horticulture")
var intro = Course(name: "Survey of Plants", in: department)
let intermediate = Course(name: "Growing Common Herbs", in: department)
let advanced = Course(name: "Caring for Tropical Plants", in: department)
intro.nextCourse = intermediate
intermediate.nextCourse = advanced
department.courses = [intro, intermediate, advanced]
/*=====================
미소유 참조와 암묵적 언래핑된 옵셔널 프로퍼티
(Unowned References and Implicitly Unwrapped Optional Properties)
======================*/
//두 개의 프로퍼티가 언제나 값을 가져야 하고 결코 nil값으로 설정되지 않아야 하는 경우
//한 클래스 내에 있는 unowned property를
//다른 클래스 내의 implicitly unwrapped optional property
//rc도 피하면서 두 개의 프로퍼티를 직접 접근할 수 있다
//Country instance가 완벽히 초기화 될 때까지 City 초기화 구문에 self를 전달 할 수 없음
class Country {
let name: String
//capitalCity property가 옵셔널 타입처럼 nil값을 기본으로 가지게 함
//unwrap없이 접근이 가능하게됨 == implitly Unwrapped
var capitalCity: City!
//Country 생성자는 name property에 값을 주면
//implicit self property를 참조하고 전달할 수 있음
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
}
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
//강한 참조 없이 country와 city 인스턴스를 하나의 구문으로 통해 생성할 수 있음
var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// "Canada's capital city is called Ottawa"
클로저에 대한 강한 참조 사이클 (Strong Reference Cycles for Closures)
/*=====================
클로저에 대한 강한 참조 사이클
(Strong Reference Cycles for Closures)
======================*/
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
let heading = HTMLElement(name: "h1")
let defaultText = "some default text"
heading.asHTML = {
return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
print(heading.asHTML())
// "<h1>some default text</h1>"
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// "<p>hello, world</p>"
paragraph = nil
// 아무 것도 출력되지 않는다. 메모리가 해제되지 않았기 때문에
클로저에 대한 강한 참조 사이클 해결 (Resolving Strong Reference Cycles for Closures)
/*=====================
클로저에 대한 강한 참조 사이클 해결
(Resolving Strong Reference Cycles for Closures)
약한 참조와 미소유 참조 (Weak and Unowned References)
======================*/
class HTMLElement1 {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
var paragraph1: HTMLElement1? = HTMLElement1(name: "p", text: "hello, world")
print(paragraph1!.asHTML())
// "<p>hello, world</p>"
paragraph1 = nil
// "p is being deinitialized"
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"