alayers2
alayers2

Reputation: 520

Memory Allocation Mystery with NSNumber boxed expression

I have a class which calculates some values, and provides a dictionary of those values as the return. The dictionary is allocated in the init of the class, and the values updated every time the algorithm runs. the return statement looks like this:

[self.retDict setObject:@(self.movmean) forKey:self.indexedKeys[0]];
[self.retDict setObject:@(self.movvar) forKey:self.indexedKeys[1]];
[self.retDict setObject:@(self.movstd) forKey:self.indexedKeys[2]];
...
[self.retDict setObject:@(0) forKey:self.indexedKeys[8]];
return self.retDict;

When I profile my app with the memory allocations tool, I see very substantial leak that traces down to these lines. Delving deeper into the call tree I can see that the boxed expression calls [NSNumber number numberWithdouble:] (which makes perfect sense...) and [NSPlaceholdernumber initWithDouble:]. This in turn calls CFNumbercreate, and seems to lead to the number of CFNumber's in my heap appearing to grow unbounded.

I suppose my ultimate question is if a NSNumber boxed expression could lead to a unreleased CFNumber? or if something else that I've done in the above code would lead to that? Any help resolving this issue would be greatly appreciated. I use boxed expressions quite often in my code, and after running for only about 10 minutes my app has almost 15MB of CFnumbers accumulated.

Upvotes: 0

Views: 499

Answers (2)

alayers2
alayers2

Reputation: 520

After bbum mentioned using the @autoreleasepool{} I did some research and came up with the answer. The app that I'm working on is the first that I've worked on that used multiple threads. We use several objects dispatched to their own thread using the active object architecture. Inside the while loop of the active objects I added a @autoreleasepool{} (as below), and all of our memory leaks went away.

while(!shutdown)
 @autoreleasepool{
  //do a bunch of work
  }
 }

I was really surprised to learn that even when ARC is turned on, when you dispatch a new thread you still need to manually create an autoreleasepool. Most of the answers about this that I saw online referenced older versions of iOS, so I wanted to post this and confirm that it is still the case in iOS 7.1.

Upvotes: 0

bbum
bbum

Reputation: 162712

Leaks tells you where an object that was leaked was allocated, not the action that caused it to leak.

If you are doing this in a loop, then it is most likely because of the lack of draining an autorelease pool. If you were to surround the above lines with @autoreleasepool { ... lines ... }, it may fix the problem.

If it is truly a leak, then it the problem lies elsewhere. Turn on "track reference counts" (sic) in the Allocations Instrument and that'll show you where the extra retains are coming from.

Upvotes: 2

Related Questions