iOS4로 뛰고 나서 마음에 드는 것 하나 - block

Apple의 세계에서 삽질/Objective-C로 삽질 2010. 9. 18. 06:21
실은 Snow Leopard 10.6에서 처음 추가된 녀석인데, 당시 대부분의 시간을 iPhone OS(당시는 이 이름이었죠. 2.2시절이었음...)에 쓰느라 별로 눈여겨 보지 않았던 부분이었죠. 4에서부터는 iOS에서도 지원되기에 관심을 두고 써보니, 확실히 내가 왜 이걸 이렇게 하고 있을까(당연하지, 3.2까지는 지원 안되니)...하는 병맛나는 노가다 코딩을 조금이나마 줄여주는 좋은 업그레이드입니다. 

말은 '좋은 업그레이드'라 했지만, 훨씬 전부터 지원했어야 되지 않나...싶은 부분이기도 하지요. 그 오랜세월 병맛나는 복사/붙이기/리팩토링 등등의 뻘짓을 시켜왔으니...

block구문의 장점은:
  1. 딱 한번 불리고 말 콜백 함수 등의 코딩을 조금이나마 덜 지저분하게 할 수 있다. 특히 개인적으로는 View나 Layer가 사라질 때 정리용 콜백으로 쓸만하더군요.
  2. 좀 더 빠를지도(...이건 상황에 따라 다를 것). 일반적으로 block은 스택영역에서 시작하니까.
  3. 꽤 많은 수의 API들이 block을 요구한다....특히, 멀티 쓰레딩/Grand Central Dispatch에선 필수. 각종 Cocoa Collection Object들의 enumeration도 block을 쓰면 좀 더 Ruby스럽게 볼만한 코드가 나오고, 멀티 코어의 장점을 좀 더 쉽게 활용 가능.

그 외에 신기능을 쓴다는 뽀대?
block과 GCD에 대한 간단한 내용을 계속해서 써보기로 하지요.
Posted by 타이가장관
,

retain/release 지옥

Apple의 세계에서 삽질/Objective-C로 삽질 2010. 9. 17. 20:12
ITObjective-C 를 시작하면서 대괄호로 인한 혼란 이후에 겪게되는 최대의 난관은 아무래도 retain/release 로 관리되는 메모리인것 같습니다. 이것들은 객체의 reference count를 증/감시키는 것인데, 이게 어찌보면 간단하면서 원시적인 메모리 관리 요령이지요(개인적으로는 반자동 관리라 부르고 있지요). 이게 때로는 매우매우 짜증나고 잡기 어려운 버그(=좀비 탄생, BAD ACCESS...--;)를 일으키는 존재라서 일각에서는 메모리 관리도 제대로 안되는 후진 플랫폼이라고 까이는 실정이지요. 저의 삽질, 그리고 나중에 들어온 H군의 삽질을 통해 알게된 점을 좀 공유해 봅니다.

retain/release 란 무엇인가?

Objective-C 객체는 내부에 retain count라는 놈을 가지고 있습니다. [object retain] 하면 retain count는 1이 증가, [object release] 하면 1이 감소...해서 retain count가 0이 되면 객체는 메모리에서 지워지지요. retain count는 어떤 다른 녀석(객체)이 어떤 객체를 가지고 있느냐...하는 ownership 비슷한 개념이라고 할 수 있겠네요. 근데 문제는 retain/release가 다른 함수(셀렉터 혹은 메시지)의 내부에서도 불리게 된다는 점이지요. 그래서 뭔가 한참 진행되고 나서는 뭐가 어떻게 되었는지 알기 어렵게 되는 상황이 비일비재합니다. 그런데...

...실은 retain/release와 관련된 대부분의 삽질과 혼란의 근원은 규칙을 잘 안지키는 것에서 일어납니다. 또 하나는 autorelease와 관련된 실수겠지요.

그럼 규칙이 뭔데?
  1. retain하면 1증가, release하면 1 감소..는 당연한 이야기
  2. alloc(후 init으로 초기화)계통의 메모리 잡는 함수는 1 증가된 녀석을 리턴(즉 retain count 1인 상태로 시작). new란 놈도 있는데 이것은 어차피 [[object alloc] init]의 축약형이므로 마찬가지지요.
  3. copy도 생성된 객체들도 마찬가지로 1 증가된 녀석을 리턴(즉 retain count 1인 상태로 시작)
  4. 그외 다른 녀석들은 모두 autoreleased object가 리턴될 것으로 가정(즉 잠시뒤까지 이 녀석이 살아있을지 어떨지는 확정지을 수 없다...그냥 너는 이미 죽어있다....라고 생각하십쇼.)
  5. 그 외에 각종 자식 객체를 갖는 놈들(view등)이나 collection 객체(set, array 이런거)에서 removeObject 비슷한 것을 부르면 remove된 객체는 release가 불립니다(가끔 이것때문에 미칩니다-오묘한 타이밍에서 뷰가 좀비가 되거나 하는 현상). 뭐 지운다는 것이 Objective-C에서는 retain count를 낮추는 것이니까...이것은 무려 Apple에서도 발생했었다고 하는 버그인 듯(특히 한 collection 에서 뽑아서 다른쪽으로 옮길 때 주의를 요함).

autorelease는 뭐지?

Objective-C의 기본적인 메모리 관리가 retain count에 의존하기 때문에 생기는 문제가 있습니다. 그에 대한 해결책(궁여지책)으로 나온 것이 autorelease라는 놈으로, release를 잠시 뒤에 하겠다는 의미입니다.  

이게 왜 필요한가...하면 바로 리턴 벨류 때문이지요. 일단 함수 등에서 리턴을 해버리면 리턴된 이후에는 리턴한 객체를 어찌해 볼 도리가 없어서 메모리 누수가 발생하게 되는데(아니면 구리게 함수 콜한 쪽에서 짝이 안맞는 release를 하는 추한 모습이 나오고...Core Foundation 시리즈에서는 좀 보임.  H군의 옛날 코드를 보면 자주 등장하기도...--;) 이를 막기 위해 궁여지책으로 좀 지연해서 release를 해 보겠다...뭐 그런 의미라고 하겠네요.

모든 어플리케이션의 시작, 이벤트(이때는 암묵적으로 프레임워크 자체에서 만들어 줌)나 쓰레드 작업이 시작될때(이 때는 명시적으로 해줘야 하는 경우가 많음) autorelease pool (NSAutoreleaePool)이란 녀석이 생긴답니다(쓰레드의 경우 때로는 직접 만들어야 됨). 객체에 autorelease 메시지를 보내면([object autorelease]) 이 객체는 release되는 대신 autorelease pool에 들어가게 됩니다. 그리고 이벤트나 쓰레드가 끝날때 autorelease pool이 release (대개는 [pool drain]을 부르지요)되면서 안에 있던 모든 객체도 다 같이 release되면서 다 같이 죽는 것이지요(물론 retain된 녀석들은 살아남지만...말이 그렇다는 겁니다).

일단 규칙을 잘 치킨다면 대부분의 경우에서 문제는 발생하지 않습니다만...

실무에서 만난 버그

메모리 누수를 관리하던 것인가...뭐 그랬을 것입니다. 근데 어떤 객체가 죽어도 삭제가 안되는 것입니다(말이 이상한가?). 제 기억엔 이 객체가 메모리 또한 엄청나게 잡아먹는 그런 녀석이었던 것으로 기억합니다. 나중에는 retain/release를 override 해서 retain count를 출력하는 등의 삽질을 한 끝에 발견한 그 원인은...

재연 나갑니다.
@interface RetainCycle : NSObject
{
	id delegate;
}

@property (nonatomic, retain) id delegate;

@end
요것만 가지고는 문제점이 다 드러나지 않지요. 실은 이 객체의 delegate 가 되는 객체가 바로 이 객체를 소유한(즉 retain하고 있는) 녀석이었다는 점. 즉 양쪽에서 서로 소유하는 형국이 되어서 한쪽이 죽기 전에 다른쪽이 죽을 수 없는 상황이 벌어진 것입니다. 이름하여...

Retain Cycle...!

통상적으로 그렇기 때문에 하위 객체에서 상위 객체를 레퍼런싱 하려 할 때라든가, delegate 경우는 retain을 하지 않는 것이 일반적입니다. 대개의 경우 상위 객체가 하위 객체를 소유하기 때문이지요. delegate도 대부분의 경우 상위 객체인 경우가 많고 더 장수하는 객체인 경우가 많으니까...만일 그렇지 않다면 님이 하신 디자인에 대해 다시 한 번 생각해 봐야 할 듯 합니다.


@interface RetainCycle : NSObject
{
	id delegate;
}

@property (nonatomic, assign) id delegate;

@end
그래서 저렇게 retain대신 assign으로 해서 문제를 해결했습니다.

요령은 없나?

규칙을 잘 지키는 것이 첫째이고, 또 하나는 Xcode에 새로 장착된 Build and Analyze를 이용해 보는 것입니다. Instruments도 필요할 때는 쓰고요.

결론

솔직히 이런 반자동 방식은 좀 병맛이랄까... 뭐 내용은 간단한데, 문제는 사람이 실수할 확률이 대단히 높다는 것. 그리고 그걸 애플식으로 기계에 사람이 맞춰가야 하는 상황이 벌어진다는 것...가비지 콜렉션이라는 것이 쉬운 것은 아니지만 적어도 사람의 실수가 날 확률이 높은 것은 문제가 될 수 있겠지요.
Posted by 타이가장관
,

개설했습니다.

카테고리 없음 2010. 9. 17. 12:41
여울님(http://sadthink.tistory.com/)의 초대로 개설할 수 있었습니다.
X글루X를 쓸까 했었는데, 그곳은 소스코드를 이쁘게 보여주는 SyntaxHighlighter가 안먹더군요. 이제는 꼼수도 안통하는지 script태그를 씹어먹어서...

그래서 티스토리로 옮겼습니다.

주로 개발(iOS등)관련해서 삽질했던 기록을 남기려고 합니다.
잘 부탁드립니다.
Posted by 타이가장관
,