CLIEN

본문 바로가기 메뉴 바로가기 보기설정 테마설정
톺아보기 공감글
커뮤니티 커뮤니티전체 C 모두의광장 F 모두의공원 I 사진게시판 Q 아무거나질문 D 정보와자료 N 새로운소식 T 유용한사이트 P 자료실 E 강좌/사용기 L 팁과강좌 U 사용기 · 체험단사용기 W 사고팔고 J 알뜰구매 S 회원중고장터 B 직접홍보 · 보험상담실 H 클리앙홈
소모임 소모임전체 ·굴러간당 ·아이포니앙 ·주식한당 ·방탄소년당 ·MaClien ·일본산당 ·개발한당 ·자전거당 ·소시당 ·AI당 ·이륜차당 ·골프당 ·소셜게임한당 ·안드로메당 ·콘솔한당 ·키보드당 ·걸그룹당 ·곰돌이당 ·나스당 ·퐁당퐁당 ·가상화폐당 ·바다건너당 ·리눅서당 ·클다방 ·노젓는당 ·디아블로당 ·위스키당 ·찰칵찍당 ·라즈베리파이당 ·육아당 ·3D메이킹 ·X세대당 ·ADHD당 ·AI그림당 ·날아간당 ·사과시계당 ·배드민턴당 ·야구당 ·농구당 ·블랙베리당 ·비어있당 ·FM당구당 ·블록체인당 ·보드게임당 ·활자중독당 ·볼링친당 ·캠핑간당 ·냐옹이당 ·문명하셨당 ·클래시앙 ·요리한당 ·쿠키런당 ·대구당 ·DANGER당 ·뚝딱뚝당 ·개판이당 ·동숲한당 ·날아올랑 ·전기자전거당 ·e북본당 ·갖고다닌당 ·이브한당 ·패셔니앙 ·물고기당 ·도시어부당 ·FM한당 ·맛있겠당 ·포뮬러당 ·젬워한당 ·안경쓴당 ·차턴당 ·총쏜당 ·땀흘린당 ·하스스톤한당 ·히어로즈한당 ·인스타한당 ·IoT당 ·KARA당 ·꼬들한당 ·덕질한당 ·어학당 ·가죽당 ·레고당 ·LOLien ·Mabinogien ·임시소모임 ·미드당 ·밀리터리당 ·땅판당 ·헌팅한당 ·오른당 ·영화본당 ·MTG한당 ·소리당 ·노키앙 ·적는당 ·방송한당 ·PC튜닝한당 ·그림그린당 ·소풍간당 ·심는당 ·패스오브엑자일당 ·품앱이당 ·리듬탄당 ·달린당 ·Sea마당 ·SimSim하당 ·심야식당 ·윈태블릿당 ·미끄러진당 ·축구당 ·나혼자산당 ·스타한당 ·스팀한당 ·파도탄당 ·테니스친당 ·테스트당 ·빨콩이당 ·공대시계당 ·여행을떠난당 ·터치패드당 ·트윗당 ·창업한당 ·VR당 ·시계찬당 ·WebOs당 ·와인마신당 ·WOW당 ·윈폰이당
임시소모임
고객지원
  • 게시물 삭제 요청
  • 불법촬영물등 신고
  • 쪽지 신고
  • 닉네임 신고
  • 제보 및 기타 제안
© CLIEN.NET
공지[점검] 잠시후 서비스 점검을 위해 약 30분간 접속이 차단됩니다. (금일 18:15 ~ 18:45)

개발한당

[iOS] 메모리 관리 : retain/release/autorelease 9

2010-11-22 23:46:31 121.♡.66.141
ebizboy

Objective C 2.0에는 자바나 C#과 같은 Garbage Collection이 도입되었지만, iOS에서는 이 기능을 지원하지 않고 

메모리 관리를 직접 해주어야 합니다.

 

C에서 포인터하면 어렵고 자바 등 GC가 지원되는 언어만 사용해서 메모리 관리가 어렵게 느껴질 수도 있지만, 

 

C나 C++에서는 malloc과 free 또는 new와 delete만 있을 뿐  메모리 관리는 직접 해야 하지만, 

 

Objective C에서는 CoreFoundation의  retain/release/autorelease 를 통해 별다른 고민없이 메모리관리를 할 수 있습니다. 원리만 잘 이해하고 원칙만 잘 지킨다면 memory leak이 나거나 미리 free가 된 곳에 접근을 해서 bad memory access 가 나는 경우는 태초부터 없앨 수 있습니다~

 

 

먼저 retain과 release에 대해서 설명하자면 아주 간단합니다.

 

retain은 해당 오브젝트의 reference값을 1 증가 시킵니다.

release는 해당 오브젝트의 reference값을 1 감소시키는데 만약 0이 되면 바로 dealloc을 호출하여 해당오브젝트는 메모리 해제가 됩니다.

 

Objective C에서 object의 라이프라이클은  처음에 alloc이 되면서 메모리할당이 되고 reference카운트 1로 시작하며  retain이 불려 값이 올라가거나 바로 release가 불려 0이 되면서 해제됩니다.

 

여기서 메모리 관리를 할때 retain을 한 곳에서 release를 한다면 메모리관리는 아주 단순해집니다. 현재 메소드내의 로직에서 object를 retain했다면 나가기전에 release만 해주면 결국 memory leak은 나지 않을테고 아무리 복잡해도 아주 단순하게 됩니다.

 

그런데 문제가 생깁니다.

 

- (NSString *)stringWithInteger:(NSInteger)num {

// 이 메소드에서 integer값을 받아 NSString으로 돌려받고 싶다고 합시다.

 NSString *ret = [[NSString alloc] initWithNSInteger:num];    // object를 retain합니다.

 [ret release];  // object를 release합니다.

 return ret;

}

 

메소드내에서 반드시 retain하면 release하도록 만들고 보니 return 될때 이미 reference count가 0이 되어 메모리에서 해제되고 죽은 오브젝트가 떨어집니다. 만약 이 오브젝트에 접근을 하면 bad memory access가 100% 발생하게 되지요.

 

 

값을 리턴해야 하므로 울며 겨자먹기로

 

- (NSString *)stringWithInteger:(NSInteger)num {

// 이 메소드에서 integer값을 받아 NSString으로 돌려받고 싶다고 합시다.

 NSString *ret = [[NSString alloc] initWithNSInteger:num];    // object를 retain합니다.

// [ret release];  // object를 release합니다.

 return ret;

}

 

받은 곳에서 release하기로 합니다.  

 

- (void)print {

  NSString *retString = [self stringWithInteger:10];

  NSLog(@"the number is %@", retString);

  [retString release];

}

 

별로 쿨하지 못합니다. stringWithInteger가 주는 NSString은 받은 곳에서 반드시 release한다고 표기라도 해 둬야겠네요.  아예 object를 return할 경우 release 의무는 전부 caller에게 넘기는 것도 방법입니다.

 

하지만 그렇게 되면 바로 위의 예처럼 먼저 object의 reference를 먼저 할당하고 사용한 다음 나중에 반드시 release를 호출해줘야하는 번거로움이 생기죠.

 

이런 메소드 본적 있지 않나요? [NSArray arrayWithObject:object];

object를 넘겨주면 그 object를 담은 NSArray를 떨굽니다. 그런데 위처럼 release를 할 필요가 없지요.

 

바로 autorelease가 사용되기 때문입니다. 

autorelease는 해당 오브젝트를 autoreleasepool에 넣어두기만 합니다. 그리고 나중에 autoreleasepool의 drain이 호출될때 release가 불려집니다.

 

autorelease를 사용해서 코드를 바꿔보면

 

 

- (NSString *)stringWithInteger:(NSInteger)num {

// 이 메소드에서 integer값을 받아 NSString으로 돌려받고 싶다고 합시다.

 NSString *ret = [[NSString alloc] initWithNSInteger:num];    // object를 retain합니다.

 [ret autorelease];

 return ret;

} 

 

- (void)print {

  NSLog(@"the number is %@", 

[self stringWithInteger:10]);

}

 

 

훨씬 깔끔해졌네요.  stringWithInteger는 이제 object를 retain하고 release모두 하게 됩니다.

print는 object release를 할 필요가 없어졌습니다.

 

 

 

자 그럼 다시 언제 release를 해야하는지 살펴 봅시다. 아주 간단합니다.

alloc또는 copy를 했다면 release를 해줍니다. 그렇지 않다면 그냥 사용합니다.

 

- (void)a {

  NSString *a = [[NSString alloc] initWithFormat:@"hello world %d", 3];

//이렇게 alloc을 불러줬다면 이 메소드가 빠져 나가기 전에 release를 해 줍니다.

  [a release];

}

만약 이 a를 리턴해야 한다면 [a autorelease];를 불러주면 되는 겁니다.     copy의 경우도 마찬가지입니다.

 

 

근데 이 경우는 어떤가요

- (void)b {

 NSString *b = [NSString stringWithFormat:@"hello world %d", 3];

}

이때는 release를 해 줄필요가 없습니다.  NSString의 stringWithFormat이라는 class method내부에서 autorelease를 해서 던져주기 때문입니다.

그리고 b에서는 alloc이나 copy를 호출하지 않았으므로 release할 의무도 없습니다.

 

 

아주 단순합니다만  위와 같은 원칙만 지켜서 코딩을 해주면 memory leak이나 bad memory access 절대 안난다고       제가 보장합니다.!!!!!  

 

메모리관리원칙 : alloc/copy 한 메소드내에서 반드시   release/autorelease를 호출한다.  

이 한가지만 지키면 됩니다.

 

 

 

 

 

그럼 여담으로 autorelease는 어떻게 작동하는지 살짝 설명해 볼까 합니다.

 

autorelease는 위에서도 설명했듯이 autoreleasepool에 푸시만 하고 release는 뒤로 미룹니다.  이렇게 autoreleasepool에 푸시된 object가 release되는 것은 autoreleasepool의 drain이 호출될 때 입니다.

(정확히는 NSAutoreleasePool 입니다.  링크 달아요 : http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html

)

 

과연 autoreleasepool의 drain은 언제 호출이 될까요?

예... autoreleasepool의 drain을 직접 호출할때 입니다. 만약 콘솔 프로그램을 한다면 autoreleasepool을 정의하고 drain을 호출하는 것은 직접 다 해줘야 합니다.

 

 

그런데 iOS 또는 Mac용 어플리케이션 프로그램을 할때는 조금 다릅니다.

int main()을 타는 콘솔어플리케이션이 아닌 NSApplication또는 UIApplication에서 시작하는 iOS/Mac 어플리케이션은 진입후 메인 쓰레드 ( 또는 UI쓰레드)는 event루프를 계속 돌게 됩니다.

event루프에서   touch이벤트나 network, disk i/o, timer 같은 이벤트가 오면 그 이벤트를 받아서 처리합니다. 그리고 이벤트 처리가 끝나면 다시 다른 이벤트를 기다리며 standby상태가 됩니다.

(추가 설명하자면 그러므로 메인쓰레드에서는 절대 시간이 많이 걸리는 일을 해서는 안됩니다. UI가 블럭되는 일이 발생하거든요... 그런 작업이 필요하다면 반드시 background thread로...)

 

이 autoreleasepool의 drain은 바로 이 이벤트처리가 끝나고 다시 다른 event를 기다릴때 호출 됩니다. 즉 하나의 이벤트처리가 끝날때마다 drain이 호출되어 autorelease를 찍어둔 것들이 죄다 release가 호출된다고 보면 됩니다. 

그러므로 stringWithFormat을 호출해서 받은 값은 이벤트로직을 완전히 빠져나가기 전까지는 계속 유효하지만

NSString *data = [NSString stringWithFormat:@"hello %d", 1]; 와 같이 data에 레퍼런스를 가지고 있다가

다음번 클릭때 사용하려고 한다면  보나마나 bad memory access가 납니다. 이미 다음번 클릭 이벤트가 오기전에 autoreleasepool의 drain이 호출되고 release가 불려서 dealloc이 되었기 때문이죠.

만약 현재의 이벤트로직을 빠져나가도 값을 유지하고 싶다면, NSString *data를 class에 instance variable로 선언하고  

data = [[NSString stringWithFormat:@"hello %d", 1] retain]; 또는 엣지있게

data = [[NSString alloc] initWithFormat:@"hello %d", 1]; 으로 씁니다.

 

그리고 - (void)dealloc {

 [data release];

 [super dealloc];

}

하면 되겠죠...

 

 

아참.... 위에서 메소드내에서 alloc/copy 한 것은 메소드가 끝나기 전에 반드시 release/autorelease한다고 했지만, 그것은 scope가 메소드내인 경우 그런 것이고 class의 instance variable이라면 위처럼 class가 dealloc될때 해 주는 것이 맞습니다. 즉 class scope가 되는 것이죠~

 

 

 

아마 이제 더 이상 memory leak,  bad memory access 구경할 일은 없겠죠?

 

혹시 궁금한게 있으면 댓글 써 주세요.  

ebizboy 님의 게시글 댓글
  • 주소복사
  • Facebook
  • X(Twitter)
댓글 • [9]
tykoh70
IP 112.♡.88.141
11-23 2010-11-23 00:03:02 / 수정일: 2017-04-30 01:45:25
·
브라보!
soyi
IP 119.♡.145.77
11-23 2010-11-23 00:26:41 / 수정일: 2017-04-30 01:45:25
·
원칙은 간단한데 실제로 프로그램을 만들다 보면 자주 실수를 하게되는 부분인것 같아요. :)
적법사
IP 175.♡.118.110
11-23 2010-11-23 00:33:59 / 수정일: 2017-04-30 01:45:25
·
아래 답변글과 이번의 강좌글을 보고 여쭤볼게 있어서 적어봅니다.

autoreleasePool을 이용하면 pool에 가지고 있다가 drain될때 전부 release한다고 이해했습니다.
보통 [pool drain]은 어플리케이션이 끝나는 지점에 선언되있더군요.(제가 아는한도에서;;)

뒤집어서 생각하면, 앱이 끝나기전에는 계속 메모리를 점유하고 있다는것 같은데요..

만약 그렇다면..앱에 따라서는 메모리 오버플로우 현상같은게 생기지는 않나요?
ebizboy
IP 121.♡.66.141
11-23 2010-11-23 00:35:48 / 수정일: 2017-04-30 01:45:25
·
적법사님//
말씀드렸듯이 iOS/MAC 어플리케이션은 메인 이벤트 루프를 돌면서 계속 drain을 호출해줍니다.

그러나 콘솔 어플리케이션은 다르지요. 직접 drain을 호출해야하는데, 결국 내부적으로 메세지 루프가 있다면 메세지 루프를 돌면서 할 가능성이 높지요.
책에 나온 예제 애플리케이션은 그냥 설명차원에서 쓰는거고 모든 것이 다 끝난 마지막에 drain을 호출 하는 것일 뿐이구요^^ 어차피 종료직전이니 해도 그만 안해도그만;

iphone을 예로 들면

UIApplication이 실행되고

for(;;) {
waitAndProcessEvent();
[autoreleasepool drain];
}


뭐 이런 느낌이지요~
적법사
IP 175.♡.118.110
11-23 2010-11-23 00:39:40 / 수정일: 2017-04-30 01:45:25
·
클리악님//아 그렇군요..마지막으로 하나만 더;; 만약 iOS 앱에서 autoreleasePool이나 release같은걸 이용해서 선언한 변수등을 해제하지 못한채로 그냥 앱을 끄게되면 그냥 그대로 메모리 상에 남아있는건가요?

그럼 해당앱을 반복적으로 끄고 켜고..하다보면 기기가 메모리 부족으로 재부팅되거나 하게될까요?

아이폰SDK 예제를 보면서 따라하는데 NSAutoreleasePool을 이용한 예제가 없어서;;
ebizboy
IP 121.♡.66.141
11-23 2010-11-23 00:40:46 / 수정일: 2017-04-30 01:45:25
·
아이폰 개발하면서 NSAutoreleasePool을 직접 다루는 일은 없다고 보시면 됩니다. (있을 수도 있습니다만;;)
autorelease만 사용하지요...

그리고 앱이 종료되면 어차피 메모리는 모두 반환됩니다.~~~ 돌아가는 동안 leak이 생기느냐 하는문제일뿐이지요 만약 앱내부에서 명시적으로 free를 안한다고 반환안되면, 앱이 강제종료되는 경우메모리가 계속 줄어들겠지요;;
적법사
IP 175.♡.118.110
11-23 2010-11-23 00:44:19 / 수정일: 2017-04-30 01:45:25
·
그렇군요..정말 감사합니다. 책에서보다 더 많은걸 배운거같아요!
ebizboy
IP 121.♡.66.141
11-23 2010-11-23 00:46:47 / 수정일: 2017-04-30 01:45:25
·
책은 대부분 설명이 부실해서요;;
그래서 제가 경험한 것들 아는 것들, 그리고 책을 봐선 알기 힘든 것들에 대해서 앞으로 쭈욱 써보려 합니다.

그래도 애플 문서들은 참 잘 되어 있습니다. 애플 문서들이 비록 영어로 도배되어 있긴 하지만 틈틈히 읽어보세요...

안드로이드는 정말 문서가 엉망인데 ㅋㅋ 애플은 문서만 보고도 왠만해선 인터넷뒤지고 할일이 없어요
회세포
IP 121.♡.205.143
03-21 2011-03-21 14:47:13 / 수정일: 2017-04-30 02:30:23
·
깔끔한 글이네요 :)
수고하셨습니다 !
새로운 댓글이 없습니다.
이미지 최대 업로드 용량 15 MB / 업로드 가능 확장자 jpg,gif,png,jpeg,webp
지나치게 큰 이미지의 크기는 조정될 수 있습니다.
목록으로
글쓰기
글쓰기
목록으로 댓글보기 이전글 다음글
아이디  ·  비밀번호 찾기 회원가입
이용규칙 운영알림판 운영소통 재검토요청 도움말 버그신고
개인정보처리방침 이용약관 책임의 한계와 법적고지 청소년 보호정책
©   •  CLIEN.NET
보안 강화를 위한 이메일 인증
안전한 서비스 이용을 위해 이메일 인증을 완료해 주세요. 현재 회원님은 이메일 인증이 완료되지 않은 상태입니다.
최근 급증하는 해킹 및 도용 시도로부터 계정을 보호하기 위해 인증 절차가 강화되었습니다.

  • 이메일 미인증 시 글쓰기, 댓글 작성 등 게시판 활동이 제한됩니다.
  • 이후 새로운 기기에서 로그인할 때마다 반드시 이메일 인증을 거쳐야 합니다.
  • 2단계 인증 사용 회원도 최초 1회는 반드시 인증하여야 합니다.
  • 개인정보에서도 이메일 인증을 할 수 있습니다.
지금 이메일 인증하기
등록된 이메일 주소를 확인하고 인증번호를 입력하여
인증을 완료해 주세요.