Cocoa 및 Objective-C를 사용한 참조 계산 이해


122

저는 이제 막 Objective-C와 Cocoa를 iPhone SDK로 플레이하기 시작했습니다. 나는 C에 합리적으로 편하다mallocfree개념에 하지만 Cocoa의 레퍼런스 카운팅 체계는 나를 다소 혼란스럽게 만든다. 일단 이해하면 매우 우아하다고 들었지만 아직 고비를 넘지 않았습니다.

어떻게 release,retainautorelease 작업 및 사용에 대한 규칙은 무엇입니까?

(또는 실패하면 무엇을 읽었습니까?

답변:


148

retain및로 시작하겠습니다 release. autorelease기본 개념을 이해하면 정말 특별한 경우입니다.

Cocoa에서 각 객체는 참조되는 횟수를 추적합니다 (특히 NSObject기본 클래스가이를 구현합니다). retain객체 를 호출 하면 참조 횟수를 1 씩 늘리고 싶다는 뜻입니다. 를 호출 release하면 객체에게 놓아주고 참조 횟수가 감소합니다. 를 호출 한 후 release참조 횟수가 이제 0이면 해당 개체의 메모리가 시스템에 의해 해제됩니다.

이것이 다른 기본적인 방법 mallocfree 특정 개체가 그들이 사용하던 메모리를 해제했기 때문에 충돌 시스템의 다른 부분에 대해 걱정할 필요가 없다는 것입니다. 모든 사람이 규칙에 따라 플레이하고 유지 / 해제한다고 가정하면 한 코드가 개체를 유지했다가 해제 할 때 개체를 참조하는 다른 코드도 영향을받지 않습니다.

때때로 혼란 스러울 수있는 것은 retain및 을 (를) 호출해야하는 상황을 아는 것 release입니다. 내 일반적인 경험 법칙은 일정 시간 동안 객체에 매달리고 싶다면 (예를 들어 클래스의 멤버 변수 인 경우) 객체의 참조 카운트가 나에 대해 알고 있는지 확인해야한다는 것입니다. 위에서 설명한 것처럼 객체의 참조 횟수는를 호출하여 증가합니다 retain. 관례 상 "init"메소드로 객체를 생성 할 때도 증가합니다 (실제로는 1로 설정). 이 두 경우 모두 release작업이 끝나면 객체 를 호출하는 것은 내 책임 입니다. 그렇지 않으면 메모리 누수가 발생합니다.

개체 생성의 예 :

NSString* s = [[NSString alloc] init];  // Ref count is 1
[s retain];                             // Ref count is 2 - silly
                                        //   to do this after init
[s release];                            // Ref count is back to 1
[s release];                            // Ref count is 0, object is freed

이제 autorelease. Autorelease는 잠시 후이 개체를 해제하도록 시스템에 알리는 편리한 (때로는 필요한) 방법으로 사용됩니다. 배관 관점에서를 autorelease호출하면 현재 스레드가 호출에 대해 NSAutoreleasePool경고를받습니다. 는 NSAutoreleasePool이제 (이벤트 루프의 현재 반복 후) 기회를 얻으면,이 호출 할 수있는 것을 알고 release개체에. 프로그래머로서 우리의 관점에서 우리를 부르는 release일을 처리하므로 우리는 할 필요가 없습니다 (실제로는 안됩니다).

주목해야 할 중요한 점은 모든 객체 생성 클래스 메서드가 자동 해제 된 객체를 반환한다는 것입니다. 예를 들어, 다음 예에서 변수 "s"는 참조 횟수가 1이지만 이벤트 루프가 완료된 후 삭제됩니다.

NSString* s = [NSString stringWithString:@"Hello World"];

해당 문자열에 매 달리려면 retain명시 적으로 호출 한 다음 release완료되면 명시 적으로 호출해야합니다 .

다음 (매우 인위적인) 코드를 고려하면 autorelease필요한 상황을 볼 수 있습니다 .

- (NSString*)createHelloWorldString
{
    NSString* s = [[NSString alloc] initWithString:@"Hello World"];

    // Now what?  We want to return s, but we've upped its reference count.
    // The caller shouldn't be responsible for releasing it, since we're the
    // ones that created it.  If we call release, however, the reference 
    // count will hit zero and bad memory will be returned to the caller.  
    // The answer is to call autorelease before returning the string.  By 
    // explicitly calling autorelease, we pass the responsibility for
    // releasing the string on to the thread's NSAutoreleasePool, which will
    // happen at some later time.  The consequence is that the returned string 
    // will still be valid for the caller of this function.
    return [s autorelease];
}

이 모든 것이 약간 혼란 스럽다는 것을 알고 있습니다.하지만 어느 시점에서는 클릭 할 것입니다. 다음은 작업을위한 몇 가지 참고 자료입니다.

  • 메모리 관리에 대한 Apple의 소개 .
  • Mac OS X 용 Cocoa 프로그래밍 (4 판) , Aaron Hillegas-훌륭한 예제가 많이있는 아주 잘 쓰여진 책. 튜토리얼처럼 읽습니다.
  • 진정으로 다이빙을하고 있다면 Big Nerd Ranch로 향할 수 있습니다. 이것은 위에서 언급 한 책의 저자 인 Aaron Hillegas가 운영하는 훈련 시설입니다. 나는 몇 년 전에 그곳에서 코코아 입문 과정을 수강했는데, 그것은 배우는 좋은 방법이었습니다.

8
당신은 "autorelease를 호출함으로써, 우리는 일시적으로 참조 횟수를 증가시킵니다"라고 썼습니다. 나는 이것이 잘못되었다고 생각합니다. autorelease는 객체가 향후 출시 될 것이라고 표시 할뿐 참조 수를 늘리지는 않습니다. cocoadev.com/index.pl?AutoRelease
LKM

2
"이제 자동 해제가 가능합니다. 자동 해제는 잠시 후이 개체를 해제하도록 시스템에 알리는 편리한 (때로는 필요한) 방법으로 사용됩니다." 도입 문장으로서 이것은 잘못된 것입니다. 시스템에 "해제 (해제)"를 지시하는 것이 아니라 보유 횟수를 줄 이도록 지시합니다.
mmalc

3
좋은 설명에 감사드립니다. 아직 명확하지 않은 한 가지입니다. 만약 NSString* s = [[NSString alloc] initWithString:@"Hello World"];다시 표시 오토 릴리즈 객체 (당신이 그것을 쓰기로) 왜이를 어떻게해야합니까 return [s autorelease];그냥 다시하지 "오토 릴리즈를"로 설정 return s?
znq 2010

3
@Stefan : 자동 해제 된 [[NSString alloc] initWithString:@"Hello World"]개체를 반환하지 않습니다. alloc호출 될 때마다 참조 횟수는 1로 설정되며 해제되었는지 확인하는 것은 해당 코드의 책임입니다. 반면에 [NSString stringWithString:]전화 오토 릴리즈 오브젝트를 돌려줍니다.
Matt Dillard

6
재미있는 퀴즈 : 대답은 @ ""와 NSString을 사용하기 때문에 문자열은 전체적으로 일정하므로 절대 보유 횟수는 일정하고 전혀 관련이 없습니다 .... 어떤 식 으로든 답을 잘못 만들지는 않습니다. 절대 보유 수는 절대로 걱정할 필요가 없다는 사실을 강조합니다.
bbum 2010

10

보유 / 해제 과정을 이해한다면 기존 Cocoa 프로그래머에게 "duh"명백한 두 가지 황금 규칙이 있지만, 불행히도 신규 사용자에게는이를 명확하게 설명하지 않습니다.

  1. 객체를 반환하는 함수에 alloc, create또는 copy이름이 있으면 해당 객체는 귀하의 것입니다. [object release]완료되면 전화해야 합니다. 또는 CFRelease(object)Core-Foundation 객체 인 경우.

  2. 이름에 이러한 단어 중 하나가 없으면 객체는 다른 사람의 것입니다. [object retain]함수가 끝난 후에도 객체를 유지 하려면 호출해야합니다 .

여러분이 직접 만든 함수에서도이 규칙을 따르는 것이 좋습니다.

(Nitpickers : 예, 안타깝게도 이러한 규칙에 대한 예외이지만 드물게 발생하는 API 호출이 몇 가지 있습니다.)


11
이것은 불완전하고 부정확합니다. 사람들이 단순히 관련 문서를 가리키는 것보다 규칙을 반복하려고하는 이유를 계속 이해하지 못합니다 : developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/…
mmalc

4
특히 Core Foundation 규칙은 Cocoa의 규칙과 다릅니다. developer.apple.com/documentation/CoreFoundation/Conceptual/…
mmalc

1
나도 동의하지 않습니다. 함수가 소유하고 싶지 않은 것을 반환하는 경우 자동 해제해야합니다. (원하는 경우) 유지하는 것은 함수 작업의 호출자입니다. 호출되는 메서드의 이름과 관련이 없어야합니다. 그것은 객체의 소유권이 명확하지 않은 더 많은 C 스타일 코딩입니다.
Sam

1
죄송합니다! 나는 내가 반대 투표에 서둘렀다 고 생각합니다. 메모리 관리 규칙 귀하의 답변은 거의 애플 문서를 인용합니다.
Sam

8

데스크톱 용 코드를 작성 중이고 Mac OS X 10.5를 대상으로 할 수 있다면 최소한 Objective-C 가비지 수집 사용을 고려해야합니다. 그것은 당신의 개발의 대부분을 정말로 단순화 할 것입니다. 그래서 Apple은 처음부터 그것을 만들고 잘 수행하도록 모든 노력을 기울였습니다.

GC를 사용하지 않을 때의 메모리 관리 규칙 :

  • 당신이 사용하여 새 개체를 만드는 경우 +alloc/+allocWithZone:, +new, -copy또는 -mutableCopy또는 경우 -retain객체, 당신이 그것의 소유권을 복용하고이 전송됩니다 확인해야합니다 -release.
  • 다른 방법으로 개체를받은 경우 해당 개체 의 소유자 가 아니며 전송 여부를 확인 해서는 안됩니다-release .
  • 객체가 전송되었는지 확인하려면 -release직접 보내거나 객체 를 보낼 수 있으며 풀이 비워 질 때 -autorelease현재 자동 해제 풀이 해당 객체 를 전송합니다 -release(수신 당 한 번 -autorelease).

일반적으로 -autorelease객체가 현재 이벤트 기간 동안 유지되도록하는 방법 으로 사용되지만 Cocoa의 이벤트 처리를 둘러싼 자동 해제 풀이 있기 때문에 나중에 정리됩니다. Cocoa에서는 호출자가 해제해야하는 객체를 반환하는 것보다 자동 해제 된 호출자에게 객체를 반환 하는 것이 훨씬 더 일반적입니다.


6

Objective-C는 Reference Counting을 사용합니다. 즉, 각 Object에는 참조 횟수가 있습니다. 개체가 생성 될 때 참조 횟수는 "1"입니다. 간단히 말해서 객체가 참조 될 때 (즉, 어딘가에 저장 됨) "보유"되며 이는 참조 횟수가 1 씩 증가 함을 의미합니다. 객체가 더 이상 필요하지 않으면 "해제"되며 이는 참조 횟수가 1 씩 감소 함을 의미합니다.

개체의 참조 횟수가 0이면 개체가 해제됩니다. 이것은 기본 참조 계산입니다.

일부 언어의 경우 참조가 자동으로 증가 및 감소하지만 Objective-c는 이러한 언어 중 하나가 아닙니다. 따라서 프로그래머는 유지 및 해제를 담당합니다.

메소드를 작성하는 일반적인 방법은 다음과 같습니다.

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;

코드 내에서 획득 한 리소스를 해제하는 것을 기억해야하는 문제는 지루하고 오류가 발생하기 쉽습니다. Objective-C는이를 훨씬 쉽게 만드는 또 다른 개념 인 Autorelease Pools를 도입했습니다. Autorelease 풀은 각 스레드에 설치되는 특수 개체입니다. NSAutoreleasePool을 찾아 보면 상당히 간단한 클래스입니다.

객체가 "autorelease"메시지를 받으면 객체는이 현재 스레드에 대해 스택에있는 자동 해제 풀을 찾습니다. 일반적으로 풀 자체가 해제되는 미래의 어느 시점에 "release"메시지를 보낼 객체로 목록에 객체를 추가합니다.

위의 코드를 사용하면 다음과 같이 말하여 더 짧고 읽기 쉽게 다시 작성할 수 있습니다.

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

객체가 자동 해제되기 때문에 더 이상 명시 적으로 "release"를 호출 할 필요가 없습니다. 이것은 일부 자동 릴리스 풀이 나중에 우리를 위해 할 것이라는 것을 알고 있기 때문입니다.

도움이 되었기를 바랍니다. Wikipedia 기사는 참조 계산에 대해 꽤 좋습니다. 자동 릴리스 풀 에 대한 자세한 내용은 여기에서 찾을 수 있습니다 . 또한 Mac OS X 10.5 이상용으로 빌드하는 경우 Xcode에 가비지 수집이 활성화 된 상태로 빌드하도록 지시하여 유지 / 릴리스 / 자동 릴리스를 완전히 무시할 수 있습니다.


2
이것은 단지 잘못된 것입니다. 표시된 예제 중 하나에서 someObject 릴리스 또는 autorlease를 보낼 필요가 없습니다.
mmalc

6

Joshua (# 6591)-Mac OS X 10.5의 가비지 수집 기능은 꽤 멋져 보이지만 iPhone에서는 사용할 수 없습니다 (또는 Mac OS X의 10.5 이전 버전에서 앱을 실행하려는 경우).

또한 라이브러리 나 재사용 할 수있는 것을 작성하는 경우 GC 모드를 사용하면 코드를 사용하는 모든 사람이 GC 모드를 사용하도록 잠그므로 내가 이해하는대로 널리 재사용 가능한 코드를 작성하려는 사람은 관리를 위해 이동하는 경향이 있습니다. 수동으로 메모리.


2
GC와 참조 카운팅을 모두 지원하는 하이브리드 프레임 워크를 작성하는 것은 완벽하게 가능합니다.
mmalc

6

여느 때와 마찬가지로 사람들이 참조 자료를 다시 말하려고 할 때 거의 항상 무언가 잘못되거나 불완전한 설명을 제공합니다.

Apple은 Memory Management Programming Guide for Cocoa 에서 Cocoa의 메모리 관리 시스템에 대한 완전한 설명을 제공하며 , 끝에는 메모리 관리 규칙에 대한 간단하지만 정확한 요약이 있습니다.




2
실제로 이것은 훨씬 더 나은 단일 페이지 요약입니다. developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…
Brian Moeskau

6

50 달러를 줄이고 Hillegass 책을 얻는 것에 대해 생각하는 것 외에는 유지 / 릴리스에 대해 구체적으로 언급하지 않겠습니다.하지만 애플리케이션 개발 초기에 Instruments 도구를 사용하는 것이 좋습니다. 첫 번째!). 이렇게하려면 실행-> 성능 도구로 시작하십시오. 사용 가능한 많은 도구 중 하나에 불과하지만 출시를 잊었을 때 표시하는 데 도움이되는 Leaks부터 시작하겠습니다. 얼마나 많은 정보가 제공 될 것인지는 그만 두었습니다. 하지만 빠르게 시작하고 진행하려면이 튜토리얼을 확인하세요.
COCOA 튜토리얼 : 기기로 메모리 누수 문제 해결

실제로 누출 을 강제로 시도 하는 것이 누출을 방지하는 방법을 배우는 더 좋은 방법 일 수 있습니다! 행운을 빕니다 ;)


5

Matt Dillard는 다음과 같이 썼습니다 .

return [[s autorelease] release];

Autorelease는 개체를 유지 하지 않습니다 . Autorelease는 나중에 해제되도록 대기열에 넣습니다. 거기에 릴리스 진술을 원하지 않습니다.




4

NilObject의 대답은 좋은 시작입니다. 다음은 수동 메모리 관리와 관련된 몇 가지 추가 정보입니다 ( iPhone에 필요 ).

개인적 alloc/init으로 개체 인 경우 참조 횟수 1이 함께 제공됩니다. 더 이상 필요하지 않은 경우에는 전화 [foo release]또는[foo autorelease] . release는이를 즉시 정리하는 반면, autorelease는 객체를 autorelease 풀에 추가하여 나중에 자동으로 해제합니다.

autorelease는 주로 문제의 객체를 반환해야하는 메서드가있는 경우 ( 수동으로 해제 할 수 없습니다. 그렇지 않으면 nil 객체를 반환하게됩니다 )하지만 계속 유지하고 싶지 않을 때 사용합니다. .

객체를 얻기 위해 alloc / init를 호출하지 않은 경우-예를 들면 다음과 같습니다.

foo = [NSString stringWithString:@"hello"];

하지만이 객체에 매달리고 싶다면 [foo preserve]를 호출해야합니다. 그렇지 않으면, 그것이 얻을 수 autoreleased있고 당신은 nil 참조를 붙들 게 될 것입니다 (위의 stringWithString예 에서처럼 ). 더 이상 필요하지 않으면으로 전화하십시오 [foo release].


2

위의 답변은 문서가 말하는 내용을 명확하게 다시 설명합니다. 대부분의 새로운 사람들이 겪는 문제는 문서화되지 않은 사건입니다. 예를 들면 :

  • Autorelease : 문서에 따르면 "미래에"릴리스가 트리거됩니다. 언제?! 기본적으로 코드를 시스템 이벤트 루프로 다시 종료 할 때까지 주변에있는 객체를 신뢰할 수 있습니다. 시스템은 현재 이벤트주기 이후 언제든지 개체를 해제 할 수 있습니다. (아까 Matt가 말한 것 같아요.)

  • 정적 문자열 : NSString *foo = @"bar";-유지하거나 해제해야합니까? 아니. 어때?

    -(void)getBar {
        return @"bar";
    }

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
  • 창조 규칙 : 당신이 그것을 만들었다면 당신이 그것을 소유하고 그것을 공개 할 것으로 예상됩니다.

일반적으로 새로운 Cocoa 프로그래머가 엉망이되는 방법은 어떤 루틴이 retainCount > 0.

다음은 Cocoa의 메모리 관리 를위한 매우 간단한 규칙 의 일부입니다 .

보존 횟수 규칙

  • 주어진 블록 내에서 -copy, -alloc 및 -retain의 사용은 -release 및 -autorelease의 사용과 동일해야합니다.
  • 편의 생성자를 사용하여 생성 된 객체 (예 : NSString의 stringWithString)는 자동 해제 된 것으로 간주됩니다.
  • -dealloc 메서드를 구현하여 소유 한 인스턴스 변수를 해제합니다.

첫 번째 글 머리 기호는 다음과 같습니다. alloc(또는 new fooCopy) 전화를 걸면 해당 개체에 대해 release를 호출해야합니다.

두 번째 글 머리 기호는 다음과 같습니다. 편의 생성자를 사용 하고 객체가 주변에 있어야하는 경우 (나중에 그릴 이미지와 마찬가지로) 유지해야합니다 (나중에 릴리스).

세 번째는 자명해야합니다.


"Autorelease : 문서에 따르면"미래에 언젠가 "릴리스가 트리거됩니다. 언제?!" 문서는 그 점에 대해 명확합니다. "autorelease는"나중에 릴리스 메시지 보내기 "를 의미합니다 (나중에 대한 일부 정의는"Autorelease 풀 "참조)." Autorelease 풀 스택에 의존하는시기가 너무 큽니다 ...
mmalc

... "시스템은 현재 이벤트주기 이후 언제든지 개체를 해제 할 수 있습니다." 이로 인해 시스템 사운드가 실제보다 덜 결정적입니다.
mmalc

... NSString foo = [self getBar]; // 여전히 유지하거나 해제 할 필요가 없습니다. 이것은 잘못되었습니다. getBar를 호출하는 사람은 구현 세부 정보를 알지 못하므로 현재 범위 밖에서 사용하려면 (일반적으로 접근자를 통해) 유지 / 해제 해야 합니다.
mmalc

"코코아의 메모리 관리를위한 매우 간단한 규칙"기사는 여러 측면에서 오래되었습니다. 특히 "편리한 생성자를 사용하여 생성 된 객체 (예 : NSString의 stringWithString)는 자동 해제 된 것으로 간주됩니다." 옳지 않습니다-단순히 "수취인이 소유하지 않음"입니다.
mmalc


0

이미 여러 사람이 언급했듯이 Apple의 Intro to Memory Management 는 시작하기에 가장 좋은 곳입니다.

내가 아직 언급하지 않은 유용한 링크 중 하나는 Practical Memory Management 입니다. 읽어 보면 애플의 문서 중간에서 찾을 수 있지만 직접 링크 할 가치가 있습니다. 예제와 일반적인 실수가 포함 된 메모리 관리 규칙에 대한 훌륭한 요약입니다 (기본적으로 여기에있는 다른 답변이 설명하려고하지만 잘 설명하지 않음).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.