티스토리 뷰

출처

http://kka7.tistory.com/21
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID48



ARC (Automatic Reference Counting)

ARC 는 클래스의 인스턴스에만 적용된다.
구조체와 열거형은 값 타입이며, 참조 타입이 아니고
참조로 저장되거나 전달되지 않는다.

class Person {

    let name: String

    init(name: String) {

        self.name = name

        print("\(name) is being initialized")

    }

    deinit {

        print("\(name) is being deinitialized")

    }

}


var reference1: Person?

var reference2: Person?

var reference3: Person?


reference1 = Person(name: "John")

reference2 = reference1

reference3 = reference1 // 3개의 강한 참조 생성


reference1 = nil

reference2 = nil

reference3 = nil    // 메모리 해제






강한 순환 참조

class Person {

    let name: String

    var apartment: Apartment?

    

    init(name: String) {

        self.name = name

    }

    

    deinit {

        print("\(name) is being deinitialized")

    }

}


class Apartment {

    let unit: String

    var tenant: Person?

    

    init(unit: String) {

        self.unit = unit

    }

    

    deinit {

        print("Apartment \(unit) is being deinitialized")

    }

}


var john: Person?

var unit4A: Apartment?


john = Person(name: "John Appleseed")

unit4A = Apartment(unit: "4A")


john!.apartment = unit4A

unit4A!.tenant = john


john = nil

unit4A = nil



//Not Deallocate




순환 참조 해결하기

2가지 해결방법으로 약한 참조와 소유하지 않는 참조를 제공한다.

weak, unowned 는 다른 인스턴스를 강하게 유지하지 않도록
순환 참조 안에서 하나의 인스턴스를 사용한다.

weak 참조는 다른 인스턴스의 생명 주기가 짧을 때 사용한다.
대조적으로 unowned 참조는 다른 인스턴스가 같은 생명주기를 가지거나
더 길게 유지될 때 사용한다.




약한 참조 (weak)

weak 참조는 참조하는 인스턴스가 메모리에서 해제되면
ARC 는 자동적으로 weak 참조를 nil 로 설정한다.

즉, weak 참조는 실시간으로 nil 로 변경되는 것이 필요하기 대문에
상수보다는 옵셔널 타입의 변수로 선언되어야한다.


ARC 가 weak 참조를 nil 로 설정할 때

프로퍼티 옵저버는 호출되지 않는다.


weak var tenant: Person?





소유하지 않은 참조 (unowned)

weak 참조와 다르게 unowned 참조는 다른 인스턴스와 같은 생명주기를 가지거나
더 긴 생명주기를 가질 때 사용된다.

unowned 참조는 항상 값을 가지고 있는 것이 예상된다.
결과적으로 ARC 는 unowned 참조의 값을 nil 로 설정하지 않으며
unowned 참조가 옵셔널 타입이 아니게 정의할 수 있다는 것을 말한다.

unowned 참조는 항상 메모리가 해제되지 않은 인스턴스를 참조하는 게 확실할 때 사용한다.
메모리가 해제된 후에 unowned 참조에 접근을 시도하면 의도하지 않은 동작을 발생시킬 수 있다.


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 john : Customer?

john = Customer(name: "John")

john!.card = CreditCard(number: 1234_5678_1234_5678,

                        customer: john!)


john = nil








소유하지 않은 참조와 암시적으로 언래핑된 옵셔널 프로퍼티

두 프로퍼티가 항상 값을 가지고 있는 경우
한번 초기화가 완료되면 nil 이 아니어야 한다.

이 경우 unowned 프로퍼티와 다른 클래스에서의 
암시적으로 언래핑된 옵셔널 프로퍼티를 결합하는 것이 유용하다.

class Country {

    let name: String

    var capitalCity: City!

    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

    }

}



강한 순환 참조를 피하면서

초기화가 한번 완료되면 옵셔널이 아닌 값처럼 접근할 수 있다.


물론, 옵셔널 ( ? ) 형태로 사용해도 문제는 없다.







클로저에서 강한 순환참조

클로저가 인스턴스의 메소드를 호출하기 때문에

캡쳐가 발생한다.


이러한 접근은 클로저가 self 를 캡쳐하게하며

강한 순환 참조를 만든다.


클래스처럼 클로저는 참조 타입이기 때문에

강한 순환 참조가 발생한다.


프로퍼티에 클로저를 할당할 때 클로저에 대한 참조를 할당한다.


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")

    }

    

}


var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")

print(paragraph!.asHTML())

// Prints "<p>hello, world</p>"


paragraph = nil


Swift 에서는 이 문제에 대해 클로저 캡쳐 목록이라는 해결책을 제공한다.




클로저에 대한 강한 순환 참조 해결하기

클로저의 선언부에서 캡쳐 목록을 정의하는 것으로 해결할 수 있다.

캡쳐 목록은 클로저 본문에 하나 이상의 참조를 캡쳐할 때

사용하는 규칙을 정의한다.


lazy var someClosure: (Int, String) -> String = {

    [unowned self, weak delegate = self.delegate!(index: Int, stringToProcess: String) -> String in

    // closure body goes here

}


lazy var someClosure: () -> String = {

    [unowned self, weak delegate = self.delegate!] in

    // closure body goes here

}





약한 참조와 소유하지 않는 참조

클로저와 인스턴스의 캡쳐가 항상 서로를 참조할 때
클로저에서 캡쳐를 unowned 참조로 정의하고
항상 같은 시점에 메모리에서 해제되어야한다.

반대로, 캡쳐된 참조가 나중에 nil 이 될 수 있다면 캡쳐를 weak 참조로 정의한다.

만약, 캡쳐된 참조가 nil 이 될 수 없다면 unowned 참조로 캡쳐하는 것이 좋다.



class HTMLElement {

    

    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 paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")

print(paragraph!.asHTML())

// Prints "<p>hello, world</p>"




공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함