본문 바로가기
프로그래밍 놀이터/iOS

[Effective Objective-C] #33 weak 참조를 사용하여 리테인 순환을 피하라

by 돼지왕 왕돼지 2017. 9. 27.
반응형

 [Effective Objective-C] #33 weak 참조를 사용하여 리테인 순환을 피하라


출처 : Effective Objective-C

arc, assign, deprecated, float, garbage collector, GC, iNT, IOS, nil, object graph, OSX, retain cycle, struct, unsafe_unretained, weak, weak 참조, zeroing pointer, [Effective Objective-C] #33 weak 참조를 사용하여 리테인 순환을 피하라, 가비지 컬렉터, 객체 그래프, 객체 타입, 리테인 순환, 메모리 누수, 비소유 관계, 상호 참조, 컬렉션, 크래시


-

객체들끼리 서로를 어떠한 방법으로 참조하여 순환을 이루고 있는 객체 그래프(object graph)에서 순환이 발생하는 것을 흔히 볼 수 있다.

순환이 발생하면 특정 지점에서 메모리 누수가 발생할 것이다.

순환을 이루는 모든 객체가 결국엔 순환 밖에 있는 객체들에 의해 참조되지 않기 때문이다.

그렇게 되면 순환 내의 객체에 접근할 수 있는 방법이 더는 없게 된다.

그뿐 아니라 순환 내의 객체들은 서로를 할당 해제하지 않을 것이다.

서로가 서로를 살아 있게 유지하기 때문이다.



-

리테인 순환 중 가장 간단한 것은 객체 두 개가 서로를 참조하는 것이다.



-

리테인 순환 내의 객체에 대한 마지막 참조가 사라지면 리테인 순환 전체가 누수가 된다.

이는 리테인 순환 내의 모든 객체에 더는 접근할 수 없음을 뜻한다.



-

맥 OS X 의 오브젝티브-C 앱은 가비지 컬렉터를 사용할 수 있었다.

가비지 컬렉터는 더 이상 접근할 수 없는 순환을 발견하면 순환 내의 모든 객체를 스스로 할당 해제한다.

그러나 맥 OS X 10.8 부터 공식적으로 가비지 컬렉터 지원이 중단되었다.

그리고 iOS 에는 가비지 컬렉터가 없다.


그 때문에 코드를 작성할 때 리테인 순환 문제를 꼭 알고 있어야 한다.

그리고 절대로 발생하게 해선 안 된다.



-

리테인 순환을 피하는 가장 좋은 방법은 weak 참조를 사용하는 것이다.

또한 이러한 참조는 비소유 관계를 표현할 때 사용할 수 있다.

이는 unsafe_unretained 프로퍼티 속성을 사용해서도 할 수 있다.



-

unsafe_unretained 속성은 프로퍼티의 값이 안전하지 않을 수 있고 인스턴스가 이 값을 리테인하지 않는다는 것을 가리킨다.

할당 해제된 프로퍼티 객체의 메서드를 호출하면 앱 크래시가 발생할 수 있다.

이 객체가 프로퍼티의 객체를 리테인하지 않았고 프로퍼티의 객체가 할당 해제되었을 수 있기 때문이다.



-

unsafe_unretained 프로퍼티 속성은 문법적으로는 assign 속성과 똑같다.

그러나 assign 은 보통 내장 타입(int, float, struct 등)에서만 사용된다.

unsafe_unretained 는 객체 타입에 주로 사용된다.

이 속성은 프로퍼티 값이 안전하지 않을 수 있다는 사실을 명확하게 나타낸다.



-

오브젝티브-C 런타임에 ARC 와 함께 생긴 새로운 기능 중 하나는 안전한 weak 참조를 만들 수 있는 기능이다.

이 weak 는 새로운 프로퍼티 속성인데 unsafe_unretained 와 완전히 동일하게 동작한다.

그러나 이 속성은 프로퍼티의 객체가 할당 해제될 때 즉시 프로퍼티 값을 nil 로 자동으로 설정해준다.



-

참조가 제거되었을 때 unsafe_unretained 속성은 금방 할당 해제될 인스턴스를 여전히 가리키지만

weak 속성을 사용하면 프로퍼티는 곧바로 nil 을 가리키게 된다.



-

그러나 weak 속성을 사용하면 지연이 허용되지 않는다.

weak 을 사용하면 코드를 안전하게 만든다.

앱이 크래시되는 대신 부정확한 데이터를 보여줄 것이다.

부정확한 데이터를 보여주는 것이 크래시되는 것보다 더 나은 사용자 경험을 줄 것이다. ( 옮긴이 주 : 글쎄..? )

그러나 weak 참조된 객체가 너무 일찍 제거되는 것은 여전히 버그다.



-

객체를 소유하지 않는다면 리테인하지 않는 것이 일반적인 규칙이다.

이 규칙의 유일한 예외는 컬렉션이다.

컬렉션은 구성 요소를 직접 소유하지 않지만 그 구성 요소 객체들이 컬렉션을 소유하는 대신 컬렉션이 그것들을 리테인한다.

객체가 소유하지 않는 무언가의 참조를 가지는 예는 델리게이트 패턴이다.




기억할 점


참조를 weak 로 만들면 리테인 순환을 피할 수 있다.


weak 참조는 자동으로 nil 로 채워질 수도, 채워지지 않을 수도 있다. ( zeroing pointer 라고 부르며 iOS 5.0 부터 그런 것은 제거되었다. )

자동으로 nil 로 설정되는 것은 ARC 에서 소개된 새로운 기능이다.

그리고 런타임에서 구현되었다.

자동으로 nil 로 채워지는 weak 참조를 읽는 것은 항상 안전하다. ( 반드시 정상 객체가 아니면 nil 이기 때문이다. )

그 말은 절대로 할당 해제되는 참조를 포함하지 않는다는 것이다.




반응형

댓글