Objective-C Reusing NSString Memory Leak - objective-c

I have written a very simple test application to try to help with a larger project I'm working on.
Simply put, the test app loops a predetermined number of times and appends "1" to a string on each loop. When the loop hits a multiple of 1000, the string is reset and the process starts over again.
The code is below; but what I am finding is that the memory usage is much higher than I would expect. Each iteration adds about .5MB.
It appears that the newString is not reused, but is discarded and a new instance created, without recovering the memory it was using.
Ultimately, the software needs to count much much higher than 100000.
As a test, if I change the iteration to 10million, it takes over 5GB memory!
Has anybody got any suggestions? So far I have various ways of writing the clearing of the string and turning off ARC and releasing it/recreating manually, but none seem to be reclaiming the amount of memory I would expect.
Thank you for any help!
*ps. Yes this actual software is totally pointless, but as I say, its a test app that will be migrated into a useful piece of code once fixed.
int targetCount = 100000;
NSString * newString;
int main(int argc, const char * argv[]) {
#autoreleasepool {
process();
return 0;
}
}
void process() {
for (int i=0; i<targetCount; i++) {
calledFunction(i);
}
}
void calledFunction(count) {
if ((count % 1000) == 0) {
newString = nil;
newString = #"";
} else {
newString = [NSString stringWithFormat:#"%#1", newString];
}
}

Your calledFunction function creates an autoreleased NSString that won't be released until the current autorelease pool gets drained.
Your process function calls the calledFunction 100,000 times in a loop. During the duration of this loop, the current autorelease pool is not given a chance to drain. By the time the end of the process method is reached, all 100,000 instances of the NSString objects created in calledFunction are still in memory.
A common solution to avoid a build-up of autoreleased objects when using a large loop is to add an additional autorelease pool as shown below:
void process() {
for (int i=0; i<targetCount; i++) {
#autoreleasepool {
calledFunction(i);
}
}
}

Your problem stems from the auto release pool, a somewhat anachronistic feature in the days of ARC.
When an object is created with an alloc/init combination the resultant object is owned by the caller. The same is true for the standard new method, it is defined as alloc followed by init.
For each init... method a class by have a matching <type>... method, this is defined as alloc/init/autorelease and returns an unowned object to the caller. For example your code uses stringWithFormat: which is defined as alloc/initWithFormat/autorelease.
The unowned returned object is in the auto release pool and unless ownership is taken it will be release automatically the next time that pool is emptied, which for the main autorelease pool is once per iteration of the main event loop. In many programs iterations of the event loop are frequent enough to reclaim objects from the auto release pool quick enough that memory usage does not climb. However if objects are created and then discarded a lot with a single iteration of the event loop, as in your example of a large for loop, the auto release pool can end up with a lot of needed objects before it is emptied.
A Common Solution
A common solution to this problem is to use a local auto release pool, which is emptied as soon as it is exited. By judicious placement of such local auto release pools memory use can be minimised. A common place for them is wrapping the body of loops which generate a lot of garbage, in your code this would be:
void process()
{
for (int i=0; i<targetCount; i++)
{ #autoreleasepool
{
calledFunction(i);
}
}
}
here all auto released and discarded objects created by calledFunction() will be reclaimed on every iteration of the loop.
A disadvantage of this approach is determining the best placement of the #autoreleasepool constructs. Surely in these days of automatic referencing count (ARC) this process can be simplified? Of course...
Another Solution: Avoid The Auto Release Pool
The problem you face is objects ending up in the auto release pool for too long, a simple solution to this is to never put the objects in the pool in the first place.
Objective-C has a third object creation pattern new..., it is similar to the <type>... but without the autorelease. Originating from the days of manual memory management and heavy auto release pool use most classes only implement new - which is just alloc/init - and no other members of the family, but you can easily add them with a category. Here is newWithFormat:
#interface NSString (ARC)
+ (instancetype)newWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
#end
#implementation NSString (ARC)
+ (instancetype)newWithFormat:(NSString *)format, ...
{
va_list args;
va_start(args, format);
id result = [[self alloc] initWithFormat:format arguments:args];
va_end(args);
return result;
}
#end
(Due to the variable arguments this is more involved than most new family methods would be.)
Add the above to your application and then replace calls to stringWithFormat with newWithFormat and the returned strings will be owned by the caller, ARC will manage them, they won't fill up the auto release pool - they will never enter it, and you won't need to figure out where to place #autoreleasepool constructs. Win, win, win :-)
HTH

Related

Auto release pool, Objective-c with C++ using it properly with multiple languages

I am learning how to use Objective-c which I have never used before with C++ and C. I am realizing that Objective-c really likes you to release objects. This is a .mm C++ based application, more C++ will be used than Objective-c.
In this example should I simply just call CFRelease(object) after it's used in the screenshot function. Or is it good practice to use #autoreleasepool within your functions.
I was not sure if #autoreleasepool releases everything regarding objects that require CFRelease.
bool screenshot() {
CGImageRef _Image;
CFStringRef _Filename, _Format;
CFURLRef _URLRef;
CGImageDestinationRef _Destination;
_Image = CGWindowListCreateImage(CGRectInfinite, (1 << 0), (0), (1 << 0));
_Filename = CFStringRef(#"screenshot.jpg");
_Format = CFStringRef(#"public.jpeg");
_URLRef = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, _Filename, CFURLPathStyle(0), 0);
_Destination = CGImageDestinationCreateWithURL(_URLRef, _Format, 1, nullptr);
CGImageDestinationAddImage(_Destination, _Image, nullptr);
// Use it here?
#autoreleasepool {
if ( CGImageDestinationFinalize(_Destination) ) {
CFRelease(_Destination); // Or individually release
CFRelease(_URLRef); // Or individually release
return true;
} else {
return false;
}
}
}
int main(int argc, const char **argv) {
// Here only, possibly?
#autoreleasepool {
screenshot();
}
return 0;
}
Any ObjC code will need a wrapping #autoreleasepool at some level. So, it's a good idea to have it in main() at the very least.
The rest of your stuff is more strictly CFFoundation, which is C and not (necessarily) Objective-C. You will need to call CFRelease on the two you have, as well as _Image, and you should not need an extra autorelease pool around them, as once the retain count goes to zero on CFRelease the memory will be deallocated directly. If you use CFAutorelease() instead, then you do need the pool, and the objects will live until the nearest wrapping autoreleasepool goes out of scope, at which point it will call CFRelease() (or the -release ObjC method) on all objects in the pool.
So an autorelease is a sort of delayed release, when you still want to use the object in the local scope afterwards. It's often used for returned values. If you are using pure CFFoundation and call CFRelease(), then there may not be anything actually autoreleased so there won't be anything in the pool.
You should be using CFRelease() (or CFAutorelease) on the three created objects regardless if the routine returns true or false -- you are done with them.
If you are using regular ObjC, they will likely autorelease objects somewhere below. You can use autoreleasepools if there is likely a large amount of memory or number of autoreleased objects you want to clean up, possibly before moving to other tasks. Or sometimes it's done once per loop.
BTW, you can use CFSTR("public.jpg") to get constant CFStringRefs, rather than converting them from constant ObjC NSStrings, but it probably works fine either way.

Is this a memory leak (using NSArray and NSCopying)

I tried something out in my code to see the effect on memory utilization. I wanted to find out if the line inside the loop was leaking. Running this loop took utilization up to 100MB and it didn't go back down again. Does this indicate a memory leak? If so why? (I'm using ARC)
for (i = 0; i < 10000000; i++)
{
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
(accounts is an array of AccountSummary objects which implements NSCopying like this: name city state phone are all NSStrings, isLocal is BOOL)
- (id)copyWithZone:(NSZone *)zone {
AccountSummary *newAccount = [[AccountSummary allocWithZone:zone] init];
newAccount.name = [self.name copyWithZone:zone];
newAccount.city = [self.city copyWithZone:zone];
newAccount.state = [self.state copyWithZone:zone];
newAccount.phone = [self.phone copyWithZone:zone];
newAccount.isLocal = self.isLocal;
return newAccount;
}
There's no leak here that I can see. There will, however, be quite a bit of peak memory usage.
The exact behaviour of things that should logically release memory varies. Quite often, instead of releasing the memory it's autorelased. (With MRR, there used to be a method called autorelease.) When you autorelease something, it isn't really released but is instead scheduled for release later, when your code is finished because it's returned to the main event loop.
If part of this is being autoreleased — and my guess is that the property assignment is autoreleasing, because autorelease is "safer" than hard releasing — that memory won't be deallocated until your next autoreleasepool flush. Code on the main thread has an autoreleasepool set up by the OS itself, so each time you return to the main event loop everything that's been autoreleased gets flushed out. Here, that probably means that all 10,000,000 copies are kept in memory until you return to the main event loop. Darn right that'll crash a real device. :)
(That's assuming you're on the main thread; if you're not, you may not even have an autorelasepool set up, which means you probably will get a leak. But I think you get warnings to console in this case, so you'd already have a hint about which way to go.)
You can reduce this peak memory usage by using #autoreleasepool:
for (i = 0; i < 10000000; i++) #autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
What will happen now is that the memory scheduled for release later in each iteration of the loop will actually be released each iteration of the loop. That should solve your immediate problem.
That said, i can't imagine why you're doing this except to check the behaviour. And if that's the case, this is unlikely your core problem.
Assuming your core problem is a leak, with ARC you're not really looking for leaks. You're looking for circular references. That means your answer likely lies elsewhere in your code. If you're sure it's self's accounts rather than dsl's accounts that are leaking, look for self being involved in a circular loop.
Also, keep in mind that calling copyWithZone: on a NSString will probably not copy the string. (There's no need to copy a read-only string, as the read-only string can't be changed. Both "copies" can be the same object.) So if you're leaking just strings, they could be associated with the original objects.
When creating lots of objects inside a loop, you should do that inside an auto release pool.
#autoreleasepool {
for (i = 0; i < 10000000; i++) {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}
or, more likely in the real world...
for (i = 0; i < 10000000; i++) {
#autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}

Objective-c pendulum modelling memory issues

I am trying to implement a modelling class for a Physics project with finite difference methods for simulating a simple pendulum. I want to be able to make this class as generic as possible so I can do whatever I want with the values on each iteration of the method. For this reason I have given my methods callback blocks which can also be used to stop the method if we want to.
For example my Euler method loop looks like so:
for (NSInteger i = 0; i < n; i++) {
if (callBack) {
if(!callBack(NO, currentTheta, currentThetaDot, currentT, (CGFloat)i/n)) break;
}
currentTheta += self.dt*f_single_theta(currentThetaDot);
currentThetaDot += self.dt*f_single_thetaDot(currentTheta, currentThetaDot, gamma);
currentT += self.dt;
}
And in the callBack block I run the code
^BOOL (BOOL complete, double theta, double thetaDot, CGFloat timeElapsed, CGFloat percentComplete){
eulerT = [eulerT stringByAppendingFormat:#"%.8f\n",timeElapsed];
eulerTheta = [eulerTheta stringByAppendingFormat:#"%.8f\n",theta];
if ((currentThetaDot*currentThetaDot + cos(currentTheta)) > 0.5) {
return 0; // stops running if total E > 0.5
}
return 1;
}];
Where eulerT and eulerTheta are strings which I later save to a file. This callback method quickly results in a massive build up of memory, even for n of order 10,000 I end up with about 1Gb of RAM usage. As soon as I comment out calling the callBack block this drops right off. Is there anyway I can keep this nice functionality without the massive memory problems?
Many people who are new to Objective C do not realize the difference between [NSArray array] and [[NSArray alloc] init]. In the days before ARC, the difference was much more obvious now. Both create a new object, but the former allocates the object, assigns it to the current NSAutoreleasePool, and leaves it with a retain count of 0 while the latter allocates it and leaves it with a retain count of 1.
Objects that are assigned to an NSAutoreleasePool do not get deallocated immediately when the retain count reaches 0. Instead, they get deallocated when the OS gets time to. Generally this can be assumed to be when control returns to the current run loop, but it can also be when drain is called on the NSAutoreleasePool.
With ARC, the difference is less obvious, but still significant. Many, if not most, of the objects your allocate are assigned to an autorelease pool. This means that you don't get them back just because you're done using them. That leads to the memory usage spiking in tight loops, such as what you posted. The solution is to explicitly drain your autorelease pool, like this:
for (NSInteger i = 0; i < n; i++) {
if (callBack) {
#autoreleasepool {
if(!callBack(NO, currentTheta, currentThetaDot, currentT, (CGFloat)i/n))
break;
}
}
currentTheta += self.dt*f_single_theta(currentThetaDot);
currentThetaDot += self.dt*f_single_thetaDot(currentTheta, currentThetaDot, gamma);
currentT += self.dt;
}
You should wrap the inside of your loop in #autoreleasepool{} to clean up temporary objects.

How to interact with #autoreleasepool

Based on Bavarious's answer to this SO question, I am led to believe that #autoreleasepool is now an Objective-C language feature if you build with LLVM/clang.
This being the case, how does one rewrite the following code to use an #autoreleasepool instead of an NSAutoreleasePool in a non-ARC environment?
[NSAutoreleasePool addObject:anObject]; // (*)
Background: I essentially want to write a custom implementation of -autorelease that does not in any way interact with the NSAutoreleasePool class:
#autoreleasepool {
SomeCls *obj = [[SomeCls alloc] init];
[obj autorelease]; // Does not go through an NSAutoreleasePool object
// ...
}
#autoreleasepool { } is a new language feature meant to obviate the need to drain the current pool at every exit point of a function. Now, instead of having to write:
void f(void) {
//Make a new pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
//Every inner scope that exits prematurely requires a drain
if (life_is_good) {
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc]init]autorelease];
//breaks out of the loop, and the function, so drain the pool
if (life_is_bad) {
[pool drain];
return;
}
}
//Life gets bad below here, so return
[pool drain];
return;
}
//end of function requires drain.
[pool drain];
}
You can wrap the entire function in an #autoreleasepool { } directive and the compiler will insert the proper drains where the function returns:
void f(void) {
//Make a new pool
#autoreleasepool {
if (life_is_good) {
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc]init]autorelease];
//breaks out of the loop, and the function, so drain the pool
if (life_is_bad) {
//[pool drain]; auto-drain
return;
}
}
//[pool drain]; auto-drain
return;
}
//[pool drain]; auto-drain
}
}
I have no idea why you would want to forgo the automatic pool mechanism, as it handles a huge amount of work for you (page management, per-thread pool management, releasing objects in a timely yet efficient manner), but it shouldn't be that much of a hassle to get a primitive NSAutoreleasePool up and running. Any autoreleasepool would require a page manager (malloc() and realloc()), pointer storage (a simple array), and some way to detect whether or not the current scope in which it is being used has exited (-drain). A simple pool could just be a thread-safe singleton, but be wary of how many objects you try to insert and remove at one time with that implementation. You just might segfault if you try to allocate too many hot pages for too many objects.
Based on Bavarious's answer to this SO question, I am led to believe that #autoreleasepool is now an Objective-C language feature if you build with LLVM/clang.
Correct.
This being the case, how does one rewrite the following code to use an #autoreleasepool instead of an NSAutoreleasePool in a non-ARC environment?
[NSAutoreleasePool addObject:anObject]; // (*)
Doc: Normally you don’t invoke this method directly—you send autorelease to object instead.
#autoreleasepool {
id anObject = ...;
[anObject autorelease];
}
Background: I essentially want to write a custom implementation of -autorelease that does not in any way interact with the NSAutoreleasePool class:
You can't even subclass NSAutoreleasePool any longer (in a meaningful way). Unless you never pass the object to other APIs which are free to autorelease, your objects are going to end up in the current autorelease pool. The mechanics of this today is not even object based.
If this is impossible, I would be open to workaround suggestions not involving NSAutoreleasePool.
Not certain what problem you are trying to solve, but you can extend lifetimes using:
NSMutableArray * fauxAutoreleasePool = NSMutableArray.new;
id anObject = ...;
[fauxAutoreleasePool addObject:anObject];
...
[fauxAutoreleasePool removeAllObjects]; // << explicitly drain, which also happens when the array is destroyed
Of course, you could write your own implementation. Why you would write your own is still a mystery, and not a good idea for production code (#autoreleasepool works well).
Though I have already marked an answer as accepted, and offered a bounty at the time, I have since found the true answer I was searching for and realised my question wasn't very clear - primarily because I didn't know what to ask.
How to interact with #autoreleasepool:
When the compiler comes across an #autoreleasepool { } block, it automatically inserts two function calls. At the opening of the block it inserts the C function call:
void *_objc_autoreleasePoolPush(void);
At the close of the #autoreleasepool block (including when a return or break is encountered within - but NOT when an exception is thrown according to the LLVM docs) the compiler inserts the C function call:
_objc_autoreleasePoolPop(void *ctxt); // ctxt is Apple's label for the param
I believe the void *ctxt parameter is an indicator of where the current #autoreleasepool started (really only useful in Apple's implementation - see here). In any case I found it can easily be ignored in custom implementations.
The -autorelease selector:
Essentially the methodology is this:
Create (if outermost block) or mark (if inner block) an autorelease pool object (in C or C++) in _objc_autoreleasePoolPush(). It seems easiest to me to use a Stack data structure for this but YMMV.
Ensure this object is in scope within the -autorelease selector, or you have a fail-safe way of accessing it.
Add the object to the autorelease pool object (in whatever way that may be) inside -autorelease.
In _objc_autoreleasePoolPop(void *), send the -release message to every item in the autorelease pool object until the marker set in _objc_autoreleasePoolPush() is reached (or you reach the bottom of the pool). Then do any additional required cleanup.
Final notes on threading:
Objective-C #autoreleasepools are supposed to be thread-local. That is, each thread has its own (if it requires one). As a result, _objc_autoreleasePoolPush() must have some way of determining whether the current thread already has an active autorelease pool and create the object if it does not.
Similarly, this is why, when creating a thread, the thread must first open an #autoreleasepool { } block before it does anything else (in Objective-C), and the final thing it must do is close that #autoreleasepool block (implicitly through break or return or with explicit fall-through).
Three final observations:
If you wish to implement your own autorelease pool object, you will need some form of thread-local storage to store it.
That last point about threading is why every Objective-C program must begin with an #autoreleasepool { } block in main().
This is why Apple ask you to use the NSThread class and GCD rather than DIY threading - they take care of all of this for you!
At the end of the #autoreleasepool block, the autoreleased objects are sent a release message.
If you want to reimplement the autorelease method, you may not want to directly send release messages to your NSObjectsubclass. If so, reimplementing release with some special behavior could do your business.
Background: I essentially want to write a custom implementation of
-autorelease that does not in any way interact with the NSAutoreleasePool class:
What makes you think that -autorelease interacts with NSAutoreleasePool?
As long as you are running on iOS 5+ or Mac OS X 10.7+, both the #autoreleasepool and -[NSObject autorelease] will use the runtime autorelease pool management functions, and have nothing to do with NSAutoreleasePool at all (and NSAutoreleasePool on those OS versions is simply a wrapper around the runtime functions).

Why is #autoreleasepool still needed with ARC?

For the most part with ARC (Automatic Reference Counting), we don't need to think about memory management at all with Objective-C objects. It is not permitted to create NSAutoreleasePools anymore, however there is a new syntax:
#autoreleasepool {
…
}
My question is, why would I ever need this when I'm not supposed to be manually releasing/autoreleasing ?
EDIT: To sum up what I got out of all the anwers and comments succinctly:
New Syntax:
#autoreleasepool { … } is new syntax for
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
More importantly:
ARC uses autorelease as well as release.
It needs an auto release pool in place to do so.
ARC doesn't create the auto release pool for you. However:
The main thread of every Cocoa app already has an autorelease pool in it.
There are two occasions when you might want to make use of #autoreleasepool:
When you are in a secondary thread and there is no auto release pool, you must make your own to prevent leaks, such as myRunLoop(…) { #autoreleasepool { … } return success; }.
When you wish to create a more local pool, as #mattjgalloway has shown in his answer.
ARC doesn't get rid of retains, releases and autoreleases, it just adds in the required ones for you. So there are still calls to retain, there are still calls to release, there are still calls to autorelease and there are still auto release pools.
One of the other changes they made with the new Clang 3.0 compiler and ARC is that they replaced NSAutoReleasePool with the #autoreleasepool compiler directive. NSAutoReleasePool was always a bit of a special "object" anyway and they made it so that the syntax of using one is not confused with an object so that it's generally a bit more simple.
So basically, you need #autoreleasepool because there are still auto release pools to worry about. You just don't need to worry about adding in autorelease calls.
An example of using an auto release pool:
- (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);
}
}
}
}
A hugely contrived example, sure, but if you didn't have the #autoreleasepool inside the outer for-loop then you'd be releasing 100000000 objects later on rather than 10000 each time round the outer for-loop.
Update:
Also see this answer - https://stackoverflow.com/a/7950636/1068248 - for why #autoreleasepool is nothing to do with ARC.
Update:
I took a look into the internals of what's going on here and wrote it up on my blog. If you take a look there then you will see exactly what ARC is doing and how the new style #autoreleasepool and how it introduces a scope is used by the compiler to infer information about what retains, releases & autoreleases are required.
#autoreleasepool doesn't autorelease anything. It creates an autorelease pool, so that when the end of block is reached, any objects that were autoreleased by ARC while the block was active will be sent release messages. Apple's Advanced Memory Management Programming Guide explains it thus:
At the end of the autorelease pool block, objects that received an autorelease message within the block are sent a release message—an object receives a release message for each time it was sent an autorelease message within the block.
People often misunderstand ARC for some kind of garbage collection or the like. The truth is that, after some time people at Apple (thanks to llvm and clang projects) realized that Objective-C's memory administration (all the retains and releases, etc.) can be fully automatized at compile time. This is, just by reading the code, even before it is run! :)
In order to do so there is only one condition: We MUST follow the rules, otherwise the compiler would not be able to automate the process at compile time. So, to ensure that we never break the rules, we are not allowed to explicitly write release, retain, etc. Those calls are Automatically injected into our code by the compiler. Hence internally we still have autoreleases, retain, release, etc. It is just we don't need to write them anymore.
The A of ARC is automatic at compile time, which is much better than at run time like garbage collection.
We still have #autoreleasepool{...} because having it does not break any of the rules, we are free create/drain our pool anytime we need it :).
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.
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.
TL;DR
Why is #autoreleasepool still needed with ARC?
#autoreleasepool is used by Objective-C and Swift to work with autorelese inside
When you work with pure Swift and allocate Swift objects - ARC handles it
But if you decide call/use Foundation/Legacy Objective-C code(NSData, Data) which uses autorelese inside then #autoreleasepool in a rescue
//Swift
let imageData = try! Data(contentsOf: url)
//Data init uses Objective-C code with [NSData dataWithContentsOfURL] which uses `autorelese`
Long answer
MRC, ARC, GC
Manual Reference Counting(MRC) or Manual Retain-Release(MRR) as a developer you are responsible for counting references on objects manually
Automatic Reference Counting(ARC) was introduced in iOS v5.0 and OS X Mountain Lion with xCode v4.2
Garbage Collection(GC) was available for Mac OS and was deprecated in OS X Mountain Lion. Must Move to ARC
Reference count in MRC and ARC
//MRC
NSLog(#"Retain Count: %d", [variable retainCount]);
//ARC
NSLog(#"Retain Count: %ld", CFGetRetainCount((__bridge CFTypeRef) variable));
Every object in heap has an integer value which indicates how many references are pointed out on it. When it equals to 0 object is deallocated by system
Allocating object
Working with Reference count
Deallocating object. deinit is called when retainCount == 0
MRC
A *a1 = [[A alloc] init]; //this A object retainCount = 1
A *a2 = a1;
[a2 retain]; //this A object retainCount = 2
// a1, a2 -> object in heap with retainCount
Correct way to release an object:
release If only this - dangling pointer. Because it still can point on the object in heap and it is possible to send a message
= nil If only this - memory leak. deinit will not be called
A *a = [[A alloc] init]; //++retainCount = 1
[a release]; //--retainCount = 0
a = nil; //guarantees that even somebody else has a reference to the object, and we try to send some message thought variable `a` this message will be just skipped
Working with Reference count(Object owner rules):
(0 -> 1) alloc, new, copy, mutableCopy
(+1) retain You are able to own an object as many times as you need(you can call retain several times)
(-1) release If you an owner you must release it. If you release more than retainCount it will be 0
(-1) autorelease Adds an object, which should be released, to autorelease pool. This pool will be processed at the end of RunLoop iteration cycle(it means when all tasks will be finished on the stack)[About] and after that release will be applied for all objects in the pool
(-1) #autoreleasepool Forces process an autorelease pool at the end of block. It is used when you deal with autorelease in a loop and want to clear resources ASAP. If you don't do it your memory footprint will be constantly increasing
autorelease is used in method calls when you allocate a new object there and return it
- (B *)foo {
B *b1 = [[B alloc] init]; //retainCount = 1
//fix - correct way - add it to fix wrong way
//[b1 autorelease];
//wrong way(without fix)
return b;
}
- (void)testFoo {
B *b2 = [a foo];
[b2 retain]; //retainCount = 2
//some logic
[b2 release]; //retainCount = 1
//Memory Leak
}
#autoreleasepool example
- (void)testFoo {
for(i=0; i<100; i++) {
B *b2 = [a foo];
//process b2
}
}
ARC
One of biggest advantage of ARC is that it automatically insert retain, release, autorelease under the hood in Compile Time and as developer you should not take care of it anymore
Enable/Disable ARC
//enable
-fobjc-arc
//disable
-fno-objc-arc
Variants from more to less priority
//1. local file - most priority
Build Phases -> Compile Sources -> Compiler Flags(Select files -> Enter)
//2. global
Build Settings -> Other C Flags(OTHER_CFLAGS)
//3. global
Build Settings -> Objective-C Automatic Reference Counting(CLANG_ENABLE_OBJC_ARC)
Check if ARC is enabled/disabled
Preprocessor __has_feature function is used
__has_feature(objc_arc)
Compile time
// error if ARC is Off. Force to enable ARC
#if ! __has_feature(objc_arc)
#error Please enable ARC for this file
#endif
//or
// error if ARC is On. Force to disable ARC
#if __has_feature(objc_arc)
#error Please disable ARC for this file
#endif
Runtime
#if __has_feature(objc_arc)
// ARC is On
NSLog(#"ARC on");
#else
// ARC is Off
NSLog(#"ARC off");
#endif
Reverse engineering(for Objective-C)
//ARC is enabled
otool -I -v <binary_path> | grep "<mrc_message>"
//e.g.
otool -I -v "/Users/alex/ARC_experiments.app/ARC_experiments" | grep "_objc_release"
//result
0x00000001000080e0 748 _objc_release
//<mrc_message>
_objc_retain
_objc_release
_objc_autoreleaseReturnValue
_objc_retainAutoreleaseReturnValue
_objc_retainAutoreleasedReturnValue
_objc_storeStrong
Tool to Migrate Objective-C MRC to ARC
ARC generates errors where you should manually remove retain, release, autorelease and others issues
Edit -> Convert -> To Objective-C ARC...
New Xcode with MRC
If you enable MRC you get next errors(warnings)(but the build will be successful)
//release/retain/autorelease/retainCount
'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'
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.