C / iOS lens: memory release with ARC (memory leak)

I am new to iOS/Objective-C and I do not understand the memory release correctly. To test it, I created an empty ARC 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 just run the test in AppDelegate :

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

The application (as expected) prints a lot of good "i = xy" numbers, but memory usage increases with each 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 memory usage increasing at all?

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

+7
source share
4 answers

A few things may go wrong:

  • You may not have ARC. You have to check it out. The easiest way is to insert -retain into your code and make sure that it throws a compiler error.

  • ARC does not necessarily prevent objects from entering the autorun pool. He tries to catch it if he can, but he gives no guarantees. It is noteworthy that at -O0 (without optimization) it often does not prevent objects from being entered into the autorun pool. This is most likely what is happening to you.

  • Even at higher levels of optimization, ARC-supported code still does not guarantee capture of auto implementations.

If you enter @autoreleasepool{} inside your for loop, you will find that memory usage should disappear. Alternatively, instead of [NSMutableString string] you can try [NSMutableString new] , which does not use the autostart pool * at all, but otherwise should behave the same in the ARC code.

* well, NSMutableString is free to use the auto resource pool if it wants

+7
source

So my question is: why is memory usage increasing at all?

Because you are doing all this selection within one cycle. All these lines are auto-implemented objects, and they will be cleared as soon as the top pool of autoresists is drained, which happens every time through the start cycle. But you do not give run run the ability to run at all, so the autostart pool is never depleted, and you do not have enough memory.

ARC frees you from worrying about managing individual objects, but you still need an understanding of how memory management works in Objective-C , since ARC follows the same rules.

+4
source

The [NSMutableString string] method returns an object with automatic implementation. This means that the object is placed in the "autorelease" pool. When the pool merges, the object will be released (and if there are no stronger references to it, it will be released). The resource pool automatically merges at the end of the startup cycle (shortly before the system goes into standby mode, waiting for another event).

When you write a loop that can highlight a large number of objects with auto-implementation, you can manage your own autostart pool:

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

This code creates a new autostart pool at the beginning of each iteration of the loop and depletes it at the end of each iteration of the loop. This way, each line you create will be released at the end of the loop, and in your code example, it will be freed, since nothing saves it.

For more information, read the Advanced Memory Management Programming Guide , in particular the chapter โ€œUsing Autostart Pool Blocksโ€.

+1
source

I donโ€™t think auto-implemented objects will be released differently because your project has ARC. Actually, the auto-calculation pool works in exactly the same way: since you select many objects inside the loop, the pool never merges during iteration.

You should try running a new autoreleasepool directly inside the loop body to see if this fixes your problem. Of course, this can be excessive, you can try dividing the loop into two nested loops so that you have an auto-fill pool from time to time, for example

 for (int i = 0; i < TOTAL_STEPS; ++i) { @autoreleasepool { for (int j = 0; j < STEP_SIZE; ++j) { .. } } } 

I donโ€™t even think that setting a local variable to nil can affect your situation, since it is a local locale for the area of โ€‹โ€‹the loop body, the compiler already knows that you cannot use it anywhere else.

+1
source

All Articles