Search Results for 'Objective-C'

2 POSTS

  1. 2010.02.26 Objective-C의 autorelease 이해하기(2) (3)
  2. 2010.02.21 Objective-C의 autorelease 이해하기(1) (4)

autorelease 이해하기(1)“에서 오토릴리즈의 기본 개념을 살펴봤다. 그 핵심은 다음과 같다.

오토릴리즈란 release 예약하고자 하는 객체를 “오토릴리즈 풀”이라 불리는 컬렉션에 등록시키는 행위이며, 오토릴리즈 풀은 자신이 해제될 때 컬렉션에 있던 객체를 모두 release한다.

그럼 iPhone 프로젝트 하나만 열어보자. 기존에 쓰던 것도 좋고, 프로젝트를 새로 만들어도 상관없다. xcode가 만들어주는 소스코드 중 main.m을 찾아보면 다음과 같은 코드가 보인다.

#import 

int main(int argc, char *argv[]) {

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // 1

int retVal = UIApplicationMain(argc, argv, nil, nil); // 2

[pool release]; // 3

return retVal;

}

이름만으로도 역할을 알 수 있는 NSAutoreleasePool 클래스가 눈에 보인다. 이것이 오토릴리즈 풀이다. 코드를 따라가보면 UIApplicationMain의 실행 전에 오토릴리즈 풀이 생성되고, 실행 후에 해제되는 것 또한 확인할 수 있다. 지난 포스트의 내용을 기억한다면 아래 문장이 술술 이해될 것이다. 이쯤되면 귀찮겠지만, 다시 한 번 개념을 확인해 본다.

UIApplicationMain내부(2)에서 autorelease한 객체들은 모두 컬렉션인 NSAutoreleasePool에 보관되며, UIApplicationMain이 종료되고 NSAutoreleasePool이 해제되는 순간(3) 해당 풀에 들어 있던 객체가 모두 release 된다. 이쯤되면 autorelease 된 객체가 해제될까 두려워 UIApplicationMain 내부에서 노심초사할 필요가 없음을 알고 있을 것이다. 뜬금없이 해제될 일은 없을테니 앞으로는 그냥 맘편히 autorelease하면 된다.

오토릴리즈 풀이 어디 숨어 있는지는 확인했고, 이제 다른 이야기를 해보자. 이전 글에서 만들었던 Employee 객체의 description 메서드를 기억하는가?

- (NSString*) description
{
NSString* desc;
desc = [[NSString alloc]
initWithFormat: @”%@(%d)”, name,age];

return ([desc autorelease]);
}

만일 오토릴리즈 풀의 해제를 한참 뒤인 UIApplicationMain이 종료되고 난 시점이 아니라, 그 전에 하고싶다면 어떻하면 되는가? 아무래도 UIApplicationMain이 종료될 때까지 해제를 기다린다는 건 상황에 따라 비효율적일 수 있다. 이런 경우, 직접 풀을 만들어 사용하면 된다.

NSAutoreleasePool * myPool = [[NSAutoreleasePool alloc] init]; // 1

NSString* employeeSummary = [employee description];

…

[myPool release]; // 2


(1)~(2) 사이에서 autorelease된 객체들은 이전의 풀이 아닌 myPool에 의해 관리된다. (2) 이후에 autorelease된 객체는 다시 이전의 풀에 의해 관리된다. 이렇게 오토릴리즈 풀은 stack처럼 최근에 만든 것이 사용되고, 최근 풀이 해제되면 다시 이전에 사용하던 풀이 그 역할을 대신하여 사용된다.

오토릴리즈를 사용하면 길건 짧건 자원 해제의 시간이 지연된다. 만일 autorelease하는 객체의 개수가 많을 경우, 객체가 가능한 빨리 release될 수 있도록 위와 같이 풀을 직접 만드는 것이 좋다. 애플은 iPhone 개발 시 autorelease의 사용을 자제해달라고 했지만, 우리는 이미(!) autorelease의 특징을 잘 알고 있으므로 상황에 맞게 적절하게 사용하면 그만이다.

신고

얼마 전 아이폰 개발에 관한 발표를 들었는데, 발표 도중 오토릴리즈(autorelease) 된 객체가 언제 소멸되느냐는 질문이 있었다. 이에 발표자는 "며느리도 알 수 없다"고 답변했다. 청중 사이에서는 자연스레 "어헉! 그럼 내가 만든 객체가 언제 소멸될지 모른단 말인가?" 등의 질문이 쏟아졌다.

Objective-C에 생소한 사람들을 대상으로 한 강의였지만, 발표자도 애매한 답을 하는 걸 보면, 오토릴리즈를 오해하는 개발자들이 꽤 있는 것 같다. 물론, 누군가를 폄하하자는 말은 결코 아니다(당시 강의를 통해 많은 도움을 받았다). 다만, 오토릴리즈의 메커니즘은 짚고 넘어갈 필요가 있다는 것이다.

아이폰 개발(Objective-C)시에는 사용하는 객체의 메모리 관리, 정확히는 참조 카운트(reference count) 관리를 해주어야 한다. 객체를 새로 생성하거나 추가로 참조할 때는 카운트를 증가(retain)시키고, 다 쓰고 나면 카운트를 감소(release)시킨다. 결국 0이 되면 객체는 메모리에서 사라진다. 참조가 0이 되면 사라지므로 그 이후에 객체를 사용하려 한다면 예외를 보게 된다. 만일 이런 내용이 생소하다면, 오토릴리즈를 이해하기 전에 Objective-C의 객체 생성/해제 시의 기본적인 참조 카운트 관리에 대해 살펴보길 바란다.

오토릴리즈는 참조 카운트 감소를 나중으로 미루기 위한, 그러면서도 카운트가 나중에 감소되는 것을 보장받기 위한 기법이다. 개인적으로 "예약 release" 정도로 표현하고 싶다. 정말 실제 release 시점을 '며느리도 모른다면' 심각한 문제가 생긴다. 예를 들어, 메모리를 꽉 채울 만큼의 객체를 생성하고 오토릴리즈를 이용해 release를 예약하였다면, 아마 며느리도 모르는 release 시점 때문에 불안해하며 객체를 더 이상 못 만들 것이다. 하나라도 더 만들면 메모리가 넘쳐(out of memory) 애플리케이션이 강제 종료될 것이기 때문이다. 좀 가정이 극단적이긴 하지만, 며느리도 모른다는 것은 사실이 아니다! 분명히 오토릴리즈는 논리적으로 명확한 release 원칙을 가지고 있다.

일단 기본적인 내용을 짚고 넘어가면, autorelease는 NSObject에 다음과 같이 정의되어 있다.

- (id)autorelease;

객체의 참조 카운트를 감소시킬 때 release대신 autorelease를 사용하면 release가 예약된다. 실제 release를 수행하면 참조 카운트가 바로 줄어들지만, autorelease 직후는 아직 release된 상황이 아니므로 참조 카운트가 감소되지 않는다(물론 나중에는 감소한다). release를 '예약'한다는 표현을 통해 이러한 사실을 짐작할 수 있다. 주제와는 관련 없는 내용이지만, autorelease라는 단어가 연상시키는 이미지, 즉 '자동'의 느낌이 오해의 근원인 것 같기도 하여, 차라리 reserveRelease 정도의 이름을 지어줬다면 어땠을까 생각해본 적이 있다.

이제 오토릴리즈가 어떤 원리로 동작하는지만 알면 된다. 알고 보면, 지극히 단순하다, 오토릴리즈된 객체는 오토릴리즈 풀(pool)에 등록된다. 오토릴리즈 풀은 객체를 관리하는 일종의 컬렉션이다. 그리고 이 컬렉션이 해제될 때 관리하는 객체를 모두 release한다. 따라서 autorelease 된 객체의 release 시점은 아주 명료하다. pool이 해제될 때이다. 허무하리만큼 쉽지 않은가? 원하면 각종 컬렉션 객체를 이용하여 pool을 간단하게 만들어 쓸 수도 있다. 다만, Foundation Framework에 NSAutoreleasePool이 있으므로 애써 만들 이유는 없다.

근본적인 질문으로 돌아가 "왜 release를 예약할까?"를 생각해보자. 간단하다. 객체를 release할 주체가 명확하지 않아 제3자인 오토릴리즈 풀에게 release를 의뢰하는 것이다. 물론 제3자인 오토릴리즈 풀은 release 시점을 정확히 알기에는 불편한 입장이므로 간단한 규칙을 하나 정한다.

"내(오토릴리즈 풀)가 죽을 때 다 죽는다."

예를 들어,임직원 정보를 출력하는 description 메서드를 다음과 같이 작성했다고 해보자.

- (NSString*) description
{
NSString* desc;
desc = [[NSString alloc]
initWithFormat: @"%@(%d)", name,age];

return (desc);
}

autorelease가 없다고 가정하고, description 메서드에서 반환하는 NSString 객체는 어디서 release해야 할까? 언뜻 두 가지의 답이 있을 것 같다.

1) description 메서드 안 : 메서드 스택을 빠져나가기 전 desc의 참조 카운트가 0이 된다. desc 가 바로 메모리에서 사라지므로, 안된다. description을 호출한 쪽에서 해제된 객체를 받게 된다.

2) description 메서드 밖: description 메서드를 호출한 쪽에서 해제하는 것인데, 가능한 시나리오지만 귀찮다. 매번 다음과 같이 description 호출할 때 마다 신경을 써 주어야 한다.

NSString* employeeSummary = [employee description];

...

[employeeSummary release];

autorelease 가 등장하면 간단하게 해결된다. description 메서드를 끝 부분만 바꾸어 다음과 같이 정의하면 된다,

- (NSString*) description
{
NSString* desc;
desc = [[NSString alloc]
initWithFormat: @"%@(%d)", name,age];

return ([desc autorelease]);
}

오토릴리즈 풀에서 객체를 해제할 것이므로 호출하는 쪽에서 별도의 관리를 할 필요가 없으며, 릴리즈 풀이 사라지지 않는 한 안전하게 description을 호출 수 있다.

이제까지 오토릴리즈의 개념과 필요성 정도를 언급했다. 하지만 아직 오토릴리즈의 풀에 대해 구체적으로 확인해 보지는 않았다. 오토릴리즈 풀이 어디에 있는지, 어떻게 객체를 관리하는지 등 알아볼 것이 좀 남았는데, 다음으로 미룬다. 다음에는 직접 풀을 사용해보고, 몇 가지 주의사항을 살펴볼 예정이다.

신고

티스토리 툴바