12

I am experiencing memory leaks linked to NSMutableArray's in a project configured to use ARC, which I thought was supposed to handle these things for you.

The following code is triggering leaks of NSNumbers:

NSMutableArray *myArray = [[NSMutableArray alloc] init];

NSNumber  *myNumber = [NSNumber numberWithFloat:10];

[myArray addObject:myNumber];

Running the last line gives the following in the debugger:

objc[1106]: Object 0x765ffe0 of class __NSCFNumber autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug

Aside from that, the object appears to be correctly added to the mutable array,

Am I doing something obvious wrong?

Note: There is one class in the project which I could not get to work with ARC, and so I excluded it from ARC using the compiler flag -fno-objc-arc. However, the leaks are occurring in other classes that are using ARC. Not sure if that is related.

Many thanks for your help.

mattjgalloway
  • 34,792
  • 12
  • 100
  • 110
Spinoxa
  • 657
  • 2
  • 7
  • 23
  • Is this code being run on a separate thread? Or in the main method outside of a `@autoreleasepool` context? – Richard J. Ross III Mar 19 '12 at 12:35
  • The only mention of @autoreleasepool in the project is in main.m. The trouble code is in other classes. How can I check if the method is on a separate thread? I haven't knowingly put it onto a separate thread but it is possible that has occurred. I built the project based on some non-official sample code utilising audio units I found online, so I'm not sure of all of the elements. – Spinoxa Mar 19 '12 at 12:43
  • Hi Richard, The leaks ARE occurring on a separate thread to main, on the same thread that is rendering the Audio Unit, which is the class excluded from arc. Is there a way I can re-direct the process to the main thread? Or do you think I should re-attempt to get the Audio Unit render to work with ARC? Many thanks for your help, I think you've pointed out the likely cause! – Spinoxa Mar 19 '12 at 13:11
  • I suppose another route I could take would be to also exclude the troublesome classes from arc using the compiler flag, and do things the old-fashioned way. What do you recommend? I'm inexperienced so wanted to use ARC if possible... – Spinoxa Mar 19 '12 at 13:16
  • Just add a @autoreleasepool when the thread begins executing, and you should be fine... – Richard J. Ross III Mar 19 '12 at 13:22
  • How can I find the point where the new thread begins to execute? What might it look like? The first thing I see mentioned on the thread is AUIOHelper::NotifyInputAvailable which I assume is a behind-the-scenes component of the audio unit rendering method, which is of type OSStatus. Where could I add @autoreleasepool? Many thanks. – Spinoxa Mar 19 '12 at 13:28
  • Just add the pool around all of your methods that execute on another thread. – Richard J. Ross III Mar 19 '12 at 13:30
  • Done it! Added @autoreleasepool as the first line in the OSStatus class and it has stopped the leaks!! Thank you so much. – Spinoxa Mar 19 '12 at 13:32

2 Answers2

39

You're probably running this code on a background thread, and don't have an autorelease pool in place. ARC will still autorelease objects for you on occasion, and if you're calling into Apple frameworks, they may still be non-ARC, so they definitely could be autoreleasing objects for you. So you still need an autorelease pool in place.

Cocoa creates an autorelease pool for you on the main thread, but doesn't do anything for you on background threads. If you're going to kick something off onto a background thread without using NSOperation or something, you'll want to wrap that thread in an @autoreleasepool, like so:

- (void)doSomething {
    [self performSelectorInBackground:@selector(backgroundSomething)];
}

- (void)backgroundSomething {
    @autoreleasepool {
        NSLog(@"Here I am in the background, doing something.");
        myArray = [[NSMutableArray alloc] init];
        // etc.
    }
}
BJ Homer
  • 48,806
  • 11
  • 116
  • 129
  • Thanks, that was indeed the problem! – Spinoxa Mar 27 '12 at 11:25
  • Excellent, thanks. Instruments Leak tool does not find these as I guess they're not technically leaks. – n13 Oct 20 '12 at 14:56
  • 1
    A little bit confusing statement about Cocoa creating autorelease pool automatically, I believe it works because `main.m` template has `@autoreleasepool {` at `int main(`. – A-Live Apr 09 '13 at 16:47
  • 1
    Well, Cocoa also has a separate autorelease pool that is created for each pass of the main thread run loop. – BJ Homer Apr 09 '13 at 17:25
  • @BJHomer thanks for the answer it helped me, however if - (void)backgroundSomething also consist of a for loop say 500 run should i use like - (void)backgroundSomething{ for(int i 500 runs){ @autorelease{...nslog(i)}}}... – maddy Nov 13 '13 at 05:17
  • 1
    @Maddy If you're concerned about memory buildup inside the `for` loop, an autorelease pool inside might be appropriate. It's still wise to have one at the root level, just to avoid any leaks. You can nest additional autorelease pools inside that one if you want. – BJ Homer Nov 13 '13 at 06:34
  • what can I do in a situation like this question http://stackoverflow.com/questions/21423309/memory-leak-in-nsstring-stringwithutf8string-with-arc-enabled – deltaaruna Feb 05 '14 at 11:07
  • I don't get it, if the idea is to just spam \@autoreleasepool everywhere, then why isn't this done by the compiler? If the compiler doesn't do this for every thread automatically, there has to be a reason to not put an \@autoreleasepool in there sometimes, right? When should or shouldn't one put an \@autoreleasepool in a thread? – Kevin Dec 08 '14 at 07:48
  • @Kevin, you need an autorelease pool if the thread will be running Objective-C code and does not already provide its own pools. A pool that wraps the entire thread's execution will not drain until the entire thread finishes; for long-running threads that would not be ideal. It might be better to instead put it inside a loop within the body of a loop, for example, so it drains periodically. The placement of the pool is the programmer's responsibility. – BJ Homer Dec 27 '14 at 14:05
-2

Very likely you have defined the NSMutableArray as a static variable. When you do that, you fall outside the bounds of any autorelease pool, since static definitions are activated outside of any runloop. ARC is not magically, it simply automates memory management calls within the framework of the existing retain/release framework and so cannot help in those cases.

The solution is to initialize the static variable somewhere in a class so that your mutable array is built within the runloop.

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • No, static variables do not cause memory leaks, if they did, then I would have a lot of them in my current project, I believe he is simply executing code outside of a @autoreleasepool context. – Richard J. Ross III Mar 19 '12 at 12:36
  • What would it look like if I have defined the NSMutableArray as a static variable? Should the array be declared in the class header file, and with what properties? Thanks – Spinoxa Mar 19 '12 at 12:53
  • It's not a memory leak. At least not as most people define one. Pretty much the only way I have come across in practice to execute code outside of an autorelease context is to declare an NSObject in a static variable - one case I know causes this exact message is declaring a static UIImage with imageNamed in a global static variable. – Kendall Helmstetter Gelner Mar 19 '12 at 16:57
  • @Spin: It would look like "static NSMutableArray *array = [[NSMutableArray alloc] init], but it would be outside the class "implementation" block. In fact even if it were not static, just a variable declared outside the implementation as global, it could cause the same issue. I have seen your EXACT error message before, so I am rather sure this is your problem... when you find the declaration and fix it please mark my answer as correct as it will be highly amusing to have a negative answer marked as correct. – Kendall Helmstetter Gelner Mar 19 '12 at 16:58
  • Hi Kendall, thanks for your suggestions but Richard's response resolved the issue: An autorelease pool needed to be set up around a secondary thread. – Spinoxa Mar 21 '12 at 11:52
  • What has the runloop got to do with ARC? Also, this answer does not at all answer the question. It's an object being autoreleased with no pool in place, as the error states. – mattjgalloway Mar 26 '12 at 22:32
  • It presents an answer to what could have been the problem, which I have seen myself. Even with ARC, if you make a static UIImage variable outside of any class, and assign it an instance with "imageNamed" you will see exactly the same message. I have left the (apparently despised) answer in place so future Google users might find out what is going wrong. Someone will thank me, eventually. – Kendall Helmstetter Gelner Mar 26 '12 at 22:49
  • Because people are mean and ignorant. – Kendall Helmstetter Gelner Feb 14 '14 at 19:31