본문 바로가기

OBJECTIVE-C

[퍼옴]멀티스레드의 메모리관리에 관한 팁.

안녕하세요. 


네온이란 네이트온 어플 개발하고있는 아레나입니다.
맨날 질문만 해대다가, 문득 멀티스레드상에서의 메모리관련을 한번 올리고자해서 감히 한번 글을 써봅니다.
(추후 개발이 완료되는 시점즈음해서 제대로 포스팅 한번 하지요)

메신져 프로그램같은경우 실시간으로 네트워크에서 메세지를 받아서 파싱후, 분류뒤에, 각자에 맞는 db에 넣은후 각자 해당하는 뷰에가서 적용을 하는 뭐 이런일의 반복입니다.

따라서 기본 한개의 스레드론 엄두도 안나구요. 채팅창이 여러개일경우 중복되면 안되고, 채팅도중 쪽지도 보내야하고 가끔 원하면 문자도 보내야 하니 스레드가 많아질수밖엔 없습니다. 네트워크에서의 메모리 절약법은(다시말해 네트워크 부하를 줄이는 방법은) 따로 다루지 않고 아이폰 개발자 카페인 만큼, 아이폰에서만 집중해서 설명하겠습니다.

아시는 분들은 다 아실만한 내용이지만, 그래도 모르시는 분들을 위해 설명드리니 참고바랍니다.^^

자 일단 문제부터 드리겠습니다.
일단 A라는 배열에 a b c d e f g h i j k l m n .......라는 문자열이 차례로 배열되어 있다고 가정합시다.

- ( NSMutableArray*) abc
{
NSMutableArray* abc=[[NSMutableArray alloc] init];
for( int i=0;i< BIGNUMBER;i++)
{
[abc addobject:[A objectAtIndex:i]];
}
return abc;
}
보통 이런식이라면, 저위에 abc는 어디에서 release 해줘야 될까요?

리턴 앞에 [abc release]; 해준다면 abc는 nil값이 반환될거고(한마디로 쓰레기값) 리턴 뒤에 해준다면 release가 동작하지 않겠지요.
그럼 어떻게 고치는게 정석일까요? abc함수를 받는 c함수를 한번 보겠습니다.

-(void) c
{
NSMutableArray* c=[self abc];
// Doing with c....
}
이렇게 된다고 가정하고, 어렵게 생각하지말고 NSMutableArray 가 포인터라는것을 생각해봅시다.

return시에 NSMutableArray의 포인터, 배열은 연속된 메모리의 공간을 의미하니까 리턴하는값은 배열이 시작되는 그 지점의 메모리가 반환이 될겁니다.

alloc 이나 retain 같은 경우라면 필요한 메모리를 딱 잡고 다른놈들이 그 메모리에 들어오지못하게 펜스를 치는개념과 비슷합니다. 
즉 펜스를 친(방어막 (? -_-;;) 이 쳐진 메모리)를 그냥 c한테 넘겨주게 되는것이죠

예제의 경우 [c release] 해주게 되면 

a에서 alloc 해준 메모리를 해지해주게 됩니다.

자 그럼 c 를 다음과 같이 바꿔보죠
-(void) c
{
NSMutableArray* c=[NSMutableArray alloc] init];
c=[self abc];
// Doing with c....
}
이럴 경우는 문법상에는 아무문제가 없지요. 하지만 이럴경우 흔히말하는 메모리 낭비가 생깁니다.
즉, 포인터개념을 다시 한번 짚어본다면, 예를들어 a의 주소가 서울시 종로구 라고 가르키고있는데, c는 강동구쯔음에 메모리를 잡고 있고 내용은 서울시 종로구 라고 되어있는거지요, 프로그램도 종로구에가서 실행시키니 별 문제는 없습니다.
하지만, [c release]를 할경우 강동구쪽 메모리만 없어질뿐, 종로구 쪽은 그대로 남아있게 되지요. 

그럼 첫번째 예제를 쓰면 안되냐구요? 물론 되지만, 멀티스레드에서는...네 안됩니다 -_-;;;
물론 프로퍼티의 속성을 copy로 잡아놓으면 메모리 복사가 된다곤 하지만, 그래도 낭비지요.

두번째 예제가 안전하긴하지만, 메모리가 많은 데스크탑이 아니고 아이팟이라는 한정적인 램을 가지고 있는 모바일 디바이스에서는 단 1kb라도 낭비하면 안되지요.
그럼 다음예제를 한번봅시다. abc함수를 다시 고쳐보는겁니다.

- ( NSMutableArray*) abc
{
NSMutableArray* abc=[NSMutableArray array];
for( int i=0;i< BIGNUMBER;i++)
{
[abc addobject:[A objectAtIndex:i]];
}
return abc;
}
이번엔 abc가 종로구가 아닌 대한민국 안에 아무때나 자리를 잡고있습니다. 하지만 펜스가 없기에 언제 autoreleasepool 이라는 놈이 튀어와서 지워버려도 할말없는 곳이지요 --; 하지만 abc의 release를 언제해야될지 걱정안해도 되는 장점이 있긴합니다. 

-(void) c
{
NSMutableArray* c=[NSMutableArray alloc] init];
c=[self abc];
// Doing with c....
}
다시 c 함수를 보지요. c는 여전히 강동구에 자리를 잡았고, abc를 가르키고있습니다. 이때 잡혀있는 abc는 앞서 말씀드렸듯, 언제 지워버려도 할말이 없지요. 따라서 c도 동작중 오류가 발생할수있습니다. 

아 그럼 도데체 어쩌란거냐-_-;; 다음이 제가생각하는 정석입니다.

- ( NSMutableArray*) abc
{
NSMutableArray* abc=[NSMutableArray array];
for( int i=0;i< BIGNUMBER;i++)
{
[abc addobject:[A objectAtIndex:i]];
}
return abc;
}
-(void) c
{
NSMutableArray* c=[NSMutableArray alloc] initWithArray:[self abc]];
// Doing with c....
}
c가 잡고있는 강동구 안에 abc내용을 가지고 들어왔습니다. 즉, abc내용이 c안으로 복사가 된것이죠. a는 언젠간 release 될터이니, (정 불안하시다면 autoreleasepool 로 한번 감싸주시면 됩니다.) a의 메모리는 걱정할 필요가 없고, 우리는 그냥 c가 alloc되어있으니, 이것만 신경쓰면 됩니다.

이렇게 되면 멀티스레드에서도 문제없이 서로간의 데이터를 주고 받을수있습니다.

물론 제 경험상 나온것이기 때문에 진짜 100% 정석과는 차이가 있을수있습니다. 하지만 leaks툴로 조사해보면 정확히 release 되는걸 알수있습니다~ 혹시라도 틀린내용있다면 지적해주시면 감사하겠습니다

그럼 모두 즐거운 코딩라이프 하십시오~

아레나 올림