얼마 전 아이폰 개발에 관한 발표를 들었는데, 발표 도중 오토릴리즈(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을 호출 수 있다.

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

« PREV : 1 : ··· : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : ··· : 87 : NEXT »