'메모리'에 해당되는 글 1건

  1. 2010.04.25 메모리 관리

메모리 관리

Objective-C 2010. 4. 25. 21:27
95년 부터 자바를 주 언어로 채택해 사용하기 시작하면서 메모리 관리에 대해 다시 고민할 일은 없으리라 생각했었는데, Objective-C때문에, 이 케케묵은 주제와 다시 마주해야 했다.

코코아 개발 그룹에서는 가비지 컬렉터를 품위 없는 것으로 생각하는 것 같으며, 퍼포먼스에 문제를 제기하고 있다. 특히 동영상 편집, 녹음등과 같은 저작도구를 킬러타이틀로 많이 가진 맥이란 점에서, 예측할 수 없는 타이밍에 걸리는 로드는 치명적이라는 것이다. 거기에 더하여 10.5 이전의 OS는 가비지 컬렉터가 아예 안 될 뿐만 아니라, 아이폰, 아이팟에서도 사용할 수 없기 때문에, 사실상 제품이 아닌 모듈 제작 입장에서는 무조건 개발자가 손수 쓰레기를 주워야 한다. (모듈을 사용하는 개발자가 GC를 쓸지 안 쓸지 알 수가 없기 때문에 그들에게 민폐를 끼치지 않으려면...)

혁신적인 아이디어를 떠올리고, 디자인하고, 구체화하기도 바빠 죽겠는데, 근 15년 만에 쓰레기까지 직접 손으로 주워야 한다니, 이 얼마나 품위 없는 짓거리인지 짜증이 확 몰아치지만, 아무튼 대세 플랫폼이니 그들의 전통을 연구해 보자.


참조수

맞다 그 참조수다. 

최첨단의 아이폰에서, 가장 오래된 메모리 관리기법에 대해 이야기하는 것이 좀 우아하지 못하긴 하지만, 어쨌든 일반적으로 C++에서 레퍼런스 카운터라고 부르는 그 참조수에 관한 이야기이다. NSObject 는 다음과 같은 두 함수를 가지고 있다.

(void) retain;
(void) release;

retain 메시지를 보내면 참조수가 1증가 한다. release 메시지를 받으면 참조수가 1 감소하며 참조수가 0이 되면 메모리에서 *자동* 으로 해제 된단다. 정확히는 dealloc 이 호출된다. (이런 정도가 자동으로 여겨지던 시절이 있긴 했다.)

객체가 생성되면 최초 1의 참조수를 갖게 된다. 

Person* p = [Person alloc];
이 수행되는 순간 p는 참조수가 1이된다.

[p release];
이제 참조수가 0이되어 메모리에서 제거된다.

상식적인 이야기이긴 하지만, 릴리즈는 콤포지트 패턴의 형태로 릴리즈된다. - 자신이 dealloc 될 때 자신의 구성 요소들을 모두 릴리즈 시킨다. - 물론 당신이 만들 클래스도 그래야 한다. 배열등과 같은 객체는 이미 그런 컴포지트 릴리즈가 구현되어 있다.


AutoRelease

코코아 책에서 오토 릴리스라는 단어를 봤을 때, 그러면 그렇지 이렇게 노역꾼 같은 일까지 개발자가 직접할 리는 없겠지, 그래 내게 우아한 자동 릴리즈를 알려줘 라고 생각했으며, 그 결과 깊은 상처를 입어야 했다.

오토 릴리스란, 메인 이벤트 루프에 도달한 후에 릴리즈되도록 예약하는 것을 말한다. UI 이벤트 루프에 비동기적으로 코드를 연결해 실행하는 것을 생각하면 될 것 같다.

이것은 특정 함수가 결과 객체를 리턴하면서 더 이상 그 객체에 대해 관심을 갖지 않는 경우(즉 리테인 할 필요가 없는경우)를 위해 생겨난 도구이다. 함수는 그 객체에 관심은 없지만 그렇다고 리턴하기 전에 릴리스하면 참조수가 0이 되어 객체가 사라져버리는 낭패가 생길 수 있다. 이 경우, 메소드를 호출한 쪽에서는 원하는 결과를 받아 볼 수 없을 것이다. 그렇다고 그냥 리턴해 버리면 불필요한 참조수가 1 증가된 상태로 결과가 전달되었으므로 좀비 객체가 될 것이다.

이럴 때 

[result autorelease]; // 여기서 그냥 release 를 했다간 객체가 증발해 버리는 낭패가 생길 수 있다.
return result;

위와 같이 하면, 스택이 메인 루프로 돌아갔을 때 예약된 autorelease 가 수행되어 참조수가 줄어들게 된다. 어떤 메소드들은 위의 예처럼 autorelease 된 객체를 리턴하는데, 이것은 순전히 개발자 마음이기 때문에 받은 결과를 내가 릴리즈해야하는지 여부는 일일히 잘 확인하는 수 밖에 없다. 아멘.


Setter와 Retain

통상적으로 한 객체가 setter함수를 통해 어떤 객체를 전달 받으면, 그 객체를 참조하고, retain 한다.
-(void) setFoo: (Foo*) newFoo{
  [newFoo retain]; 
  [foo release];
  foo = newFoo;
}

retain을 먼저하는 것은 중요한 사안이다. 만약 newFoo와 foo가 같은 객체에 대한 포인터였다면, 릴리즈를 먼저하는 순간 객체를 잃어버리게 될 것이기 때문이다. Objective-C는 C++의 확장이기 때문에 제품의 기능과는 무관한 이런 상황들을 개발자가 세밀하게 고려해야만 한다.

또 foo를 릴리스할 때 이것이 nil이었다고 하더라도 Objective-C에서는 아무 에러도 없이 그냥 지나가게 된다. 함수가 동적으로 매핑되기 때문에 null로 전달 되는 메시지는 자동적으로 무시된다. 그야 가장 보편적인 실수의 형태에 대해 시스템이 죽는 것을 방지하기 위한 조치로 생각 되지만, 디버깅을 개발자의 무덤으로 만드는 정책으로 여겨지는 것 또한 사실이다.

'Objective-C' 카테고리의 다른 글

아이폰 앱 개발시 코어 데이터 도입 타당성  (0) 2010.08.02
멀티 태스킹 지원시 주의점  (0) 2010.07.23
X-Code의 인터페이스 빌더  (0) 2010.04.28
Getter Method  (0) 2010.04.25
Posted by 지이이이율
,