왜 @autoreleasepool이 ARC에 여전히 필요한가요?
ARC (Automatic Reference Counting)를 사용하는 대부분의 경우 Objective-C 객체를 사용하여 메모리 관리에 대해 전혀 생각할 필요가 없습니다. NSAutoreleasePool
더 이상 을 (를) 만들 수는 없지만 새로운 구문이 있습니다.
@autoreleasepool {
…
}
내 질문은 수동으로 릴리스 / 자동 해제하지 않아야 할 때 왜 이것이 필요할까요?
편집 : 모든 anwers 및 주석에서 간결하게 얻은 것을 요약하면 다음과 같습니다.
새로운 구문 :
@autoreleasepool { … }
의 새로운 구문입니다
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
더 중요한 것은:
- ARC는
autorelease
물론을 사용합니다release
. - 이를 위해서는 자동 풀 풀이 필요합니다.
- ARC는 자동 릴리스 풀을 생성하지 않습니다. 하나:
- 모든 Cocoa 앱의 메인 스레드에는 이미 자동 릴리스 풀이 있습니다.
- 다음과 같은 두 가지 경우를 활용할 수 있습니다
@autoreleasepool
.- 보조 스레드에 있고 자동 릴리스 풀이없는 경우 등의 누출을 방지하기 위해 자체적으로 만들어야합니다
myRunLoop(…) { @autoreleasepool { … } return success; }
. - @mattjgalloway가 그의 답변에 표시된 것처럼 더 많은 로컬 풀을 만들려면.
- 보조 스레드에 있고 자동 릴리스 풀이없는 경우 등의 누출을 방지하기 위해 자체적으로 만들어야합니다
ARC는 유지, 릴리스 및 자동 릴리스를 제거하지 않고 필요한 것을 추가합니다. 따라서 계속 유지해야하는 통화가 있고, 해제 할 통화가 있으며, 자동 릴리스에 대한 통화가 있으며 자동 릴리스 풀이 있습니다.
그들은 새로운 연타 3.0 컴파일러와 ARC로 만든 다른 변화 중 하나는 대체한다는 것입니다 NSAutoReleasePool
와 @autoreleasepool
컴파일러 지시문. NSAutoReleasePool
어쨌든 항상 특별한 "객체"였고 그것들을 사용하는 구문이 객체와 혼동되지 않도록하여 일반적으로 조금 더 간단했습니다.
따라서 기본적으로 @autoreleasepool
여전히 걱정할 자동 릴리스 풀이 있기 때문에 필요합니다 . autorelease
통화 추가에 대해 걱정할 필요가 없습니다 .
자동 릴리스 풀을 사용하는 예 :
- (void)useALoadOfNumbers {
for (int j = 0; j < 10000; ++j) {
@autoreleasepool {
for (int i = 0; i < 10000; ++i) {
NSNumber *number = [NSNumber numberWithInt:(i+j)];
NSLog(@"number = %p", number);
}
}
}
}
확실히 고안된 예이지만 @autoreleasepool
, 외부 for
루프 내부가 없다면 외부 루프를 돌 때마다 10000이 아닌 나중에 100000000 개의 객체를 릴리스 할 것 for
입니다.
업데이트 : ARC와 관련이없는 이유 는 https://stackoverflow.com/a/7950636/1068248- 이 답변을 참조하십시오 @autoreleasepool
.
업데이트 : 나는 여기에서 무슨 일이 일어나고 있는지 내부를 살펴보고 내 블로그에 썼습니다 . 거기에서 살펴보면 ARC가 수행하는 작업과 새로운 스타일 @autoreleasepool
및 범위를 도입하는 방법이 컴파일러에서 유지, 릴리스 및 자동 릴리스가 필요한 항목에 대한 정보를 유추하는 데 사용되는 방식을 정확하게 알 수 있습니다.
@autoreleasepool
아무것도 자동 해제하지 않습니다. 자동 릴리스 풀을 작성하여 블록 끝에 도달하면 블록이 활성 상태 인 동안 ARC에 의해 자동 릴리스 된 모든 오브젝트에 릴리스 메시지가 전송됩니다. Apple의 Advanced Memory Management Programming Guide 는 다음과 같이 설명합니다.
자동 릴리스 풀 블록의 끝에서 블록 내에서 자동 릴리스 메시지를 수신 한 개체는 릴리스 메시지를 전송합니다. 개체는 블록 내에서 자동 릴리스 메시지가 전송 될 때마다 릴리스 메시지를받습니다.
사람들은 종종 가비지 수집 등의 이유로 ARC를 오해합니다. 진실은 애플의 사람들 (llvm과 clang 프로젝트 덕분에)이 Objective-C의 메모리 관리 ( retains
및 모든 releases
등등)가 컴파일 타임에 완전히 자동화 될 수 있다는 것을 깨달았다 . 이것은 코드가 실행되기 전에도 코드를 읽는 것입니다! :)
그렇게하기 위해서는 단 하나의 조건 만 있습니다 : 규칙을 따라야합니다 . 그렇지 않으면 컴파일러가 컴파일 타임에 프로세스를 자동화 할 수 없습니다. 그럼 우리가 수 있도록 결코 규칙을 위반하지, 우리는 명시 적으로 쓰기에 허용되지 않습니다 release
, retain
등, 그 호출은 컴파일러에 의해 자동 우리의 코드로 주입된다. 따라서 내부적으로 우리는 여전히이 autorelease
들, retain
, release
, 등 단지 우리는 더 이상을 작성하지 않아도됩니다.
ARC의 A는 컴파일 타임에 자동으로 실행되므로 가비지 수집과 같은 런타임보다 훨씬 좋습니다.
우리는 여전히 @autoreleasepool{...}
규칙을 위반하지 않기 때문에 언제든지 풀을 생성 / 배수 할 수 있습니다. :).
It's because you still need to provide the compiler with hints about when it is safe for autoreleased objects to go out of scope.
Quoted from https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:
Autorelease Pool Blocks and Threads
Each thread in a Cocoa application maintains its own stack of autorelease pool blocks. If you are writing a Foundation-only program or if you detach a thread, you need to create your own autorelease pool block.
If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should use autorelease pool blocks (like AppKit and UIKit do on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If your detached thread does not make Cocoa calls, you do not need to use an autorelease pool block.
Note: If you create secondary threads using the POSIX thread APIs instead of NSThread, you cannot use Cocoa unless Cocoa is in multithreading mode. Cocoa enters multithreading mode only after detaching its first NSThread object. To use Cocoa on secondary POSIX threads, your application must first detach at least one NSThread object, which can immediately exit. You can test whether Cocoa is in multithreading mode with the NSThread class method isMultiThreaded.
...
In Automatic Reference Counting, or ARC, the system uses the same reference counting system as MRR, but it insertsthe appropriate memory management method callsfor you at compile-time. You are strongly encouraged to use ARC for new projects. If you use ARC, there is typically no need to understand the underlying implementation described in this document, although it may in some situations be helpful. For more about ARC, see Transitioning to ARC Release Notes.
Autorelease pools are required for returning newly created objects from a method. E.g. consider this piece of code:
- (NSString *)messageOfTheDay {
return [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
}
The string created in the method will have a retain count of one. Now who shall balance that retain count with a release?
The method itself? Not possible, it has to return the created object, so it must not release it prior to returning.
The caller of the method? The caller does not expect to retrieve an object that needs releasing, the method name does not imply that a new object is created, it only says that an object is returned and this returned object may be a new one requiring a release but it may as well be an existing one that doesn't. What the method does return may even depend on some internal state, so the the caller cannot know if it has to release that object and it shouldn't have to care.
If the caller had to always release all returned object by convention, then every object not newly created would always have to be retained prior to returning it from a method and it would have to be released by the caller once it goes out of scope, unless it is returned again. This would be highly inefficient in many cases as one can completely avoid altering retain counts in many cases if the caller will not always release the returned object.
That's why there are autorelease pools, so the first method will in fact become
- (NSString *)messageOfTheDay {
NSString * res = [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
return [res autorelease];
}
Calling autorelease
on an object adds it to the autorelease pool, but what does that really mean, adding an object to the autorelease pool? Well, it means telling your system "I want you to to release that object for me but at some later time, not now; it has a retain count that needs to be balanced by a release otherwise memory will leak but I cannot do that myself right now, as I need the object to stay alive beyond my current scope and my caller won't do it for me either, it has no knowledge that this needs to be done. So add it to your pool and once you clean up that pool, also clean up my object for me."
With ARC the compiler decides for you when to retain an object, when to release an object and when to add it to an autorelease pool but it still requires the presence of autorelease pools to be able to return newly created objects from methods without leaking memory. Apple has just made some nifty optimizations to the generated code which will sometimes eliminate autorelease pools during runtime. These optimizations require that both, the caller and the callee are using ARC (remember mixing ARC and non-ARC is legal and also officially supported) and if that is actually the case can only be known at runtime.
Consider this ARC Code:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
The code that the system generates, can either behave like the following code (that is the safe version that allows you to freely mix ARC and non-ARC code):
// Callee
- (SomeObject *)getSomeObject {
return [[[SomeObject alloc] init] autorelease];
}
// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];
(Note the retain/release in the caller is just a defensive safety retain, it's not strictly required, the code would be perfectly correct without it)
Or it can behave like this code, in case that both are detected to use ARC at runtime:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];
As you can see, Apple eliminates the atuorelease, thus also the delayed object release when the pool is destroyed, as well as the safety retain. To learn more about how that is possible and what's really going on behind the scenes, check out this blog post.
Now to the actual question: Why would one use @autoreleasepool
?
For most developers, there's only one reason left today for using this construct in their code and that is to keep the memory footprint small where applicable. E.g. consider this loop:
for (int i = 0; i < 1000000; i++) {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
Assume that every call to tempObjectForData
may create a new TempObject
that is returned autorelease. The for-loop will create one million of these temp objects which are all collected in the current autoreleasepool and only once that pool is destroyed, all the temp objects are destroyed as well. Until that happens, you have one million of these temp objects in memory.
If you write the code like this instead:
for (int i = 0; i < 1000000; i++) @autoreleasepool {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
Then a new pool is created every time the for-loop runs and is destroyed at the end of each loop iteration. That way at most one temp object is hanging around in memory at any time despite the loop running one million times.
In the past you often had to also manage autoreleasepools yourself when managing threads (e.g. using NSThread
) as only the main thread automatically has an autorelease pool for a Cocoa/UIKit app. Yet this is pretty much legacy today as today you probably wouldn't use threads to begin with. You'd use GCD DispatchQueue
's or NSOperationQueue
's and these two both do manage a top level autorelease pool for you, created before running a block/task and destroyed once done with it.
There seems to be a lot of confusion on this topic (and at least 80 people who probably are now confused about this and think they need to sprinkle @autoreleasepool around their code).
If a project (including its dependencies) exclusively uses ARC, then @autoreleasepool never needs to be used and will do nothing useful. ARC will handle releasing objects at the correct time. For example:
@interface Testing: NSObject
+ (void) test;
@end
@implementation Testing
- (void) dealloc { NSLog(@"dealloc"); }
+ (void) test
{
while(true) NSLog(@"p = %p", [Testing new]);
}
@end
displays:
p = 0x17696f80
dealloc
p = 0x17570a90
dealloc
Each Testing object is deallocated as soon as the value goes out of scope, without waiting for an autorelease pool to be exited. (The same thing happens with the NSNumber example; this just lets us observe the dealloc.) ARC does not use autorelease.
The reason @autoreleasepool is still allowed is for mixed ARC and non-ARC projects, which haven't yet completely transitioned to ARC.
If you call into non-ARC code, it may return an autoreleased object. In that case, the above loop would leak, since the current autorelease pool will never be exited. That's where you'd want to put an @autoreleasepool around the code block.
But if you've completely made the ARC transition, then forget about autoreleasepool.
참고URL : https://stackoverflow.com/questions/9086913/why-is-autoreleasepool-still-needed-with-arc
'Programming' 카테고리의 다른 글
Linux에 어떤 LaTeX Editor를 추천하십니까? (0) | 2020.05.13 |
---|---|
Amazon SimpleDB 및 Amazon DynamoDB (0) | 2020.05.13 |
내 앱 또는 해당 종속성이 Android 광고 ID 정책을 위반합니까? (0) | 2020.05.13 |
SQL 구문은 대소 문자를 구분합니까? (0) | 2020.05.13 |
정수 나누기의 동작은 무엇입니까? (0) | 2020.05.13 |