Leif Leif - 5 months ago 45
iOS Question

Objective C / iOS: Memory release with ARC (memory leak)

I am new to

iOS/Objective-C
and I do not understand the release of memory correctly.
To test it, I created an empty
ARC
enabled iPhone-Project and created a very simple test class:

#import "MemTest.h"

@implementation MemTest {

}

-(void) start {
for (int i = 0; i < 1500000; i++) {
NSMutableString *myString = [NSMutableString string];

// The appended string is 2000 characters long in the real test class.
[myString appendString:@"12345678901234567890123456 <very long>"];

if (i % 1000 == 0) {
NSLog(@"i = %d", i);
}

myString = nil;
}
}

@end


I simply start the test in the
AppDelegate
:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
MemTest *test = [[MemTest alloc] init];
[test start];

....
}


The application (as expected) prints many nice numbers "i = xy" but the memory usage increases with every iteration and finally the application crashes:

....
2012-12-06 20:17:40.193 MemTestApp[19250:11303] i = 930000
2012-12-06 20:17:40.208 MemTestApp[19250:11303] i = 931000
MemTestApp(19250,0xac63f2c0) malloc: *** mmap(size=16777216) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug


So my question is: Why is the memory usage growing at all?

I thought by assigning nil, the memory should be released, when using
ARC
. What am I missing here?

Answer

A few things could be going wrong:

  1. You might not actually have ARC enabled. You should double-check that. Simplest way is to throw in a -retain in your code and make sure that throws a compiler error.

  2. ARC doesn't necessarily prevent objects from entering the autorelease pool. It tries to catch that if it can, but it doesn't make guarantees. Notably, at -O0 (no optimization), it frequently does not prevent objects from entering the autorelease pool. This is most likely what's going on for you.

  3. Even at higher optimization levels, ARC-enabled code is still not guaranteed to catch autoreleases.

If you stick an @autoreleasepool{} inside of your for loop you'll find the memory usage should go away. Alternatively, instead of using [NSMutableString string], you could try [NSMutableString new], which doesn't use the autorelease pool at all* but should otherwise behave identically in ARC code.

* well, NSMutableString is free to use the autorelease pool internally if it wants to