I've built a simple trivia game using ARC. While profiling its memory usage using the Allocations profiling tool in Xcode, I see that memory is not always freed. For one example of the problem, I have a class for an ActivePlayer object:
ActivePlayer.h:
@interface ActivePlayer : NSObject
@property (nonatomic, strong) NSString * name;
@property (nonatomic) NSInteger overallScore;
@property (nonatomic) NSInteger questionScore;
- (id) initWithName:(NSString *)name;
@end
ActivePlayer.m:
#import "ActivePlayer.h"
@interface ActivePlayer ()
@end
@implementation ActivePlayer
- (id) initWithName:(NSString *)name
{
self = [self init];
if (self) {
self.name = name;
self.overallScore = 0;
}
return self;
}
/*
- (void)dealloc
{
self.name = nil;
}
*/
@end
And the ActivePlayer is created in a createPlayer method in an ActiveGame class:
[[ActivePlayer alloc] initWithName:name]
I'm executing the following test case: I start a new game (which allocates one ActivePlayer), I answer one question, and then the game ends (and at this point the ActivePlayer is deallocated). I can then start another game and repeat this cycle (each cycle is a "game", as described below). While using the Allocations profiling tool, what I expect to see is that memory has been allocated in the middle of a game but has been deallocated after the game ends (no matter how many times I play a game). But I've found this is not always the case:
BTW: each bulleted row below describes a row in the Objects List tab of the Allocations tool; this site won't let me post a screenshot, hence the text description. All rows are Live; I'm only viewing Created and Still Living allocations.
While game #1 is in progress, I see the following allocations.
- Category=ActivePlayer; Size=16; Responsible Caller=-[ActiveGame createPlayer:]
- Category=Malloc 48 Bytes; Size=48; Responsible Caller=-[ActivePlayer initWithName:]
After game #1 is complete, I see the following. The ActivePlayer object has been deallocated, but the 48 bytes is still Live.
- Category=Malloc 48 Bytes; Size=48; Responsible Caller=-[ActivePlayer initWithName:]
If I start game #2, I see the following while the game is in progress. There are two new allocations in addition to the one from game #1.
- Category=Malloc 48 Bytes; Size=48; Responsible Caller=-[ActivePlayer initWithName:]
- Category=ActivePlayer; Size=16; Responsible Caller=-[ActiveGame createPlayer:]
- Category=Malloc 144 Bytes; Size=144; Responsible Caller=-[ActivePlayer initWithName:]
And after game #2 is complete, I see the following. Again, the ActivePlayer object has been deallocated, but the "Malloc X Bytes" allocations still exist.
- Category=Malloc 48 Bytes; Size=48; Responsible Caller=-[ActivePlayer initWithName:]
- Category=Malloc 144 Bytes; Size=144; Responsible Caller=-[ActivePlayer initWithName:]
After that, I get unusual results -- if I play games #3, #4, and #5, I never see in-game rows for Category="Malloc X Bytes", only a new row for Category=ActivePlayer, which is freed up after the game ends. The first two "Malloc" rows, as shown above, continue to persist. I've also seen other odd behavior -- while testing this yesterday using the iPhone 6.0 Simulator, live memory was left behind only after games #2 and #3, but not games #1, #4, and #5. So while memory remains allocated, the times at which it occurs seem to vary across my device and different versions of the simulator.
And my questions:
- Is my understanding correct that I shouldn't be seeing any live memory from the call to initWithPlayer after the game ends and the ActivePlayer object has been freed?
- If yes, what's causing it, and how do I deallocate it?
- Or do I not need to worry about it at all?
Notes:
- These screenshots come from running my app on an iPhone 4 running iOS 6.1. But I see similar behavior running with the iPhone Simulator for 5.1, 6.0, and 6.1, and I saw it on my iPhone running iOS 6.0 before I upgraded.
- In ActivePlayer.m, the dealloc method is currently commented out, though I've tested while it's been uncommented and have verified that it's being called (by the system; I don't directly call dealloc anywhere). Either way, the behavior is the same.
- For what it's worth, nothing is reported by the Leaks profiling tool.
- While this is one example that results in 192 bytes of live memory that I believe should be freed, I'm seeing this with many of my classes, i.e. it appears that memory allocation grows over time, which I would think is a problem.