iOS - XCode 4.4 - Potential Memory Leaks Using Analyze - ios

I ran analyze on my 1st iPhone app, and I see a few potential memory leaks. The app itself works fine on the simulator.
I would like to do the right thing and clear the potential memory leaks, but some are quite puzzling. Maybe someone could help me here?
Thanks in advance.
Pier.
Error 1) The Analyzer says "Potential leak of an object stored in tempDate and tempAns"
#import "Answer.h"
#implementation Answer
#synthesize answerTiming;
#synthesize xPosition;
#synthesize earlyOrLate;
#synthesize hit;
+ (Answer *) createAnswerWithTiming :(NSDate *)paramTiming andXPosition :(float) xPosition
{
NSDate * tempDate = [[NSDate alloc] initWithTimeInterval:0 sinceDate:paramTiming];
Answer * tempAns = [[Answer alloc] init ];
[tempAns setAnswerTiming:tempDate];
[tempDate release];
[tempAns setXPosition:xPosition];
[tempAns setEarlyOrLate:0];
[tempAns setHit:false];
return tempAns;
}
- (void)dealloc {
[answerTiming release];
[self release];
[super dealloc];
}
#end
Error 2) Analyzer says (see below)
- (void)viewDidLoad
{
[super viewDidLoad];
........
...
UIImage * perfectImage = [UIImage imageNamed: #"perfect.png"];
self.perfectImageView2 = [[UIImageView alloc]initWithImage:perfectImage];
// method returns an objective C content with a +1 retain count
[self.perfectImageView2 setFrame:CGRectMake(145.0f,
150.0f,
self.perfectImageView2.image.size.width,
self.perfectImageView2.image.size.height)];
self.view.backgroundColor = [UIColor whiteColor];
UIImage * levelUpImage = [UIImage imageNamed:#"levelup.png"];
self.levelUpImageView = [[UIImageView alloc] initWithImage:levelUpImage];
[self.levelUpImageView setFrame:CGRectMake(100.0f,
400.0f,
self.levelUpImageView.image.size.width,
self.levelUpImageView.image.size.height)];
//object leaked, allocated object is not referenced later in this execution path and has a retain count of +1
self.view.backgroundColor = [UIColor whiteColor];
}
Error 3)
- (NSMutableArray *) generateQuestionTapAnswers:(NSString *) answersString withFirstBeat: (NSDate *) firstBeatTime
{
NSArray * notesToDraw = [answersString componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: #" "]];
float noteValueOffset = 0.0;
NSMutableArray * answerArray = [[NSMutableArray alloc] init ];
// Method returns an objective C object with a +1 retain count
for (int i=1; i < notesToDraw.count; i++) // i = 0 is the time signature
{
.....
}
return answerArray;
// Object returned to caller as an owning reference (single retain count transferred to caller)
// Object leaked: Object allocated and stored into answerArray is returned from a method whose name generateQuestionTapAnswers does not start with copy, mutableCopy
}

Regarding your first and third warnings, the compiler will assume that you'll be creating an object in "a method whose name begins with alloc, new, copy, or mutableCopy" (see Advanced Memory Management Programming Guide). So, if you're returning a +1 object, make sure your method name begins with one of those four prefixes. If you create a +1 object without one of those prefixes, it won't be happy. So, for error #1, if you rename that method to be something like newAnswerWithTiming, that warning should go away. (If the calling method doesn't clean up after them, though, the warning will just be shifted to that routine, but let's take it one step at a time.)
Likewise for error #3, if you rename that method to be something like newAnswerArrayFromQuestionTapAnswers (or whatever ... I'm not sure if I understand your method name ... just make sure it starts with new), you'll similarly eliminate that warning. In both cases, just make sure to release it at the appropriate point because whomever called these two respective methods now "owns" those objects and is responsible for their cleanup.
As an aside, you don't need to do [self release] in your dealloc in your discussion of error #1. (Frankly, I'm surprised it doesn't generate an error.) You don't need it because if self wasn't already at retainCount of zero, you never would have gotten to dealloc in the first place.
Regarding error #2, how are the properties perfectImageView2 and levelUpImageView defined? If retain or strong, you're creating something with a +2 retainCount, and you probably want to add an autorelease after the alloc/init.

Related

Memory management and properties (init/dealloc)

Until yesterday I thought I understood how properties memory management works, but then I ran an "Analize" task with XCode and got plenty of "This object is not own here". Here is a simple example that describes my problem :
MyObservingObject.h:
#interface MyObservingObject : NSObject
#property(nonatomic, retain) NSMutableDictionary *observedDictionary;
-(id)initWithDictCapacity:(int)capacity;
#end
MyObservingObject.m:
#synthesize observedDictionary;
-(id)initWithDictCapacity:(int)capacity {
self = [super init];
if (self) {
self.observedDictionary = [[[NSMutableDictionary alloc] initWithCapacity:capacity] autorelease];
}
return self;
}
- (void)dealloc {
// The following line makes the Analize action say :
// "Incorrect decrement of the reference count of an object that is not owned at this point by the caller"
[self.observedDictionary release], self.observedDictionary=nil;
[super dealloc];
}
What I don't understand is Why should I leave this property without calling release on it? My #property is set as retain (copy does the same), so when I'm doing self.myRetainProperty = X, then X got its retain count increased (it's owned by self), didn't it ?
You should let the setter do the releasing for you, so remove the call to release in dealloc:
- (void)dealloc {
self.observedDictionary=nil;
[super dealloc];
}
This is because the setter will be synthensized to something like:
- (void)setObject:(id)object
{
[object retain];
[_object release];
_object = object;
}
Which will work as desired when you pass in nil.
It did get increased, but when you set it to nil, the setter method first releases the backing instance variable, and only then does it retain and assign the new value. Thus setting the property to nil is enough, setting the ivar to nil leaks memory, though.
For your better understanding: the typical implementation of an autogenerated retaining setter is equivalent to something like
- (void)setFoo:(id)foo
{
if (_foo != foo) {
[_foo release];
_foo = [foo retain];
}
}
Also note that, as a consequence, you should never release properties like this. If you do so, the backing ivar may be deallocated, and messaging it (release by the accessor when setting the property to nil afterwards) can crash.
You don't need to do
[self.observedDictionary release]
before
self.observedDictionary=nil;
This is enough, because this is a property, and it will automatically send release to previous value
self.observedDictionary=nil;
The reason for the compiler warning is because of the way you are retrieving the object.
By calling
[self.observedDictionary release];
you are in fact going through the accessor method defined as
- (NSDictionary *)observedDictionary;
This returns your object but due to the naming of observedDictionary the compiler assumes that there is no transfer of ownership e.g. the callee will not have to release this object unless they take a further retain. It is because of this that the compiler thinks you are going to do an overrelease by releasing an object that you don't actually own.
More specifically the convention for method names that transfer ownership is for them to start with copy, mutableCopy, alloc or new.
Some examples
Here I have used a name that does not imply transfer for ownership so I get a warning
- (id)object;
{
return [[NSObject alloc] init];
}
//=> Object leaked: allocated object is returned from a method whose name ('object') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa
Fix 1: (don't transfer ownership)
- (id)object;
{
return [[[NSObject alloc] init] autorelease];
}
Fix 2: (make the name more appropriate)
- (id)newObject;
{
return [[NSObject alloc] init];
}
With this knowledge we can of naming convention we can see that the below is wrong because we do not own the returned object
[self.object release]; //=> Produced warnings
And to show a final example - releasing an object that implies ownership transfer with it's name
[self.newObject release]; //=> No Warning

Incorrect decrement of reference count not owned at this point

I don't understand this one unless it's because I'm releasing the property instead of the ivar. Can someone shed light on the problem?
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
[self.dataToBeLoaded release];
The warning is Incorrect decrement of the reference count of an object that is not owned by the caller.
The dataToBeLoaded property has the retain attribute associated with its setter.
My understanding is the the alloc init increments the retain count and the property assignment increments the retain count. Since I only one to retain it once, that's why I release it immediately after the assignment.
UPDATE -- some experimental results:
Since I noted in my comments below that I have received contradictory advice on what the retain property does to the synthesized setter, I thought I would do a little experiment using the code above, modified with some logging:
NSLog(#"retain 1 = %d", [dataToBeLoaded_ retainCount]);
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 2 = %d", [dataToBeLoaded_ retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [dataToBeLoaded_ retainCount]);
The results at each log statement were 0, 2, and 1.
Apparently, it's not possible to step into the alloc or the init code to see the retain count go from 0 to 1 to 2. I could have subclassed the NSMutableData class, but I was short on time.
I know a lot is said that you can't rely on the value of the retainCount property, but what I have seems consistent and I would expect reasonable behavior over the short scope of the code like that shown in the example. So I'm inclined to believe that prior advice is correct -- the retain property is a promise to include a retain within the setter. So here I have the retain from the alloc/init and the retain from the call to the setter. Hence, the retain count is set to 2.
When I run this code:
NSMutableData *theData;
NSLog(#"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(#"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [theData retainCount]);
The retain count at each log statement is 0, 1, 2, 1.
So I have evidence that suggests the setter is providing a retain. This appear to be more of a promise than a hint, because it is actually happening.
I'm open to other explanations. I don't want to be arrogant about this. I just want to get it right as to what is happening. I appears that the warning (in the subject of this question) is really spurious and not something to worry about.
One more experiment is done using assign rather than retain as an attribute in the #property statement. With the same code:
NSMutableData *theData;
NSLog(#"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(#"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [theData retainCount]);
The retain count at each log is 0, 1, 1 (the setter did not retain), then the error message: message sent to deallocated instance. The last release had set the retain count to zero, which triggered the deallocation.
UPDATE 2
A final update -- when the synthesized setter is overridden with your own code, the retain attribute is no longer observed unless your setter explicitly includes it. Apparently (and this contradicts what I had been told in other threads here) you have to include your own retain in the setter if that's what you want. While I didn't test it here, you probably need to release the old instance first, or it will be leaked.
This custom setter no longer has the property attributes of the #propety declaration:
- (void) setDataToBeLoaded:(NSMutableData *)dataToBeLoaded {
dataToBeLoaded_ = dataToBeLoaded;
}
This makes sense. Override a synthesized setter and you override all of the declared properties. Use a synthesized setter, and the declared properties are observed in the synthesized implementation.
The #property attributes represent a "promise" as to how the synthesized setter is implemented. Once you write a custom setter, you're on your own.
The key is to think through what the below code is doing. I'll write it out in full for clarity:
[self setDataToBeLoaded:[[NSMutableData alloc] initWithLength:10000]];
This creates an object with a +1 retain count and passes it to setDataToBeLoaded:. (*) It then throws away its reference to that object, leaking it.
[[self dataToBeLoaded] release];
This calls dataToBeLoaded and releases the object returned. There is no promise whatsoever that the object returned by dataToBeLoaded is the same as the object passed to setDataToBeLoaded:. You probably think they're the same, and looking at your code you can probably convince yourself that it will always work out that way, but that's not an API promise.
The code posted by Antwan is correct:
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.dataToBeLoaded = data;
[data release];
That creates an object with a +1 retain count. Then passes it to a method, then releases it.
Or, if you're willing to use the autorelease pool, you can simplify it to:
self.dataToBeLoaded = [NSMutableData dataWithLength:1000];
(*) Technically this passes a message to self that may or may not cause this method to be called, but that muddies the issue. For most purposes, pretend it's a method call. But do not pretend that it just sets the property. It really is going to call some method.
EDIT:
Maybe this code will make the issue a little clearer. It's indicative of common caching solutions:
.h
#interface MYObject : NSObject
#property (nonatomic, readwrite, strong) NSString *stuff;
#end
.m
#interface MYObject ()
#property (nonatomic, readwrite, weak) MYStuffManager *manager;
#implementation MYObject
... Initialize manager ...
- (NSString*)stuff {
return [self.manager stuffForObject:self];
}
- (void)setStuff:(NSString *)stuff {
[self.manager setStuff:stuff forObject:self];
}
Now maybe manager does some foolery in the background. Maybe it caches various copies of stuff. Maybe it copies them. Maybe it wraps them into other objects. What's important is that you can't rely on -stuff always returning the same object you passed to -setStuff:. So you certainly shouldn't release it.
Note that nothing in the header indicates this, and nothing should. It's not the callers' business. But if the caller releases the result of -stuff, then you will get hard-to-debug crashes.
#synthesize is just a shorthand for writing some tedious code (code that implements stuff and setStuff: as reading and writing an ivar). But nothing says that you have to use #synthesize for your properties.
My guess would be that the method
- (NSMutableData *)dataToBeLoaded;
does not contain any of the memory management keywords therefore it is assumed that you do not own the data returned and therefore should not be releasing it.
Either use
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.dataToBeLoaded = data;
[data release]; data = nil;
or if you can why not lazy load it when you actually need it?
- (NSMutableData *)dataToBeLoaded;
{
if (!_dataToBeLoaded) {
_dataToBeLoaded = [[NSMutableData alloc] initWithLength:1000];
}
return _dataToBeLoaded;
}
It just means that you are releasing an object you don't own.
I'd say call it with the instance var directly instead of using the getter, but not sure whether that will fix your analyses warnings. Also why not use [NSMutableData dataWithLength:1000]; which is autoreleased and therefore eliminates the need of that extra release call ( and would probably get rid of that warning too! )
other ways you could fix it:
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.databToBeLoaded = data;
[data release];
I provided a couple of updates that I think answer what is going on here. With some test results, my conclusion is that this warning is spurious, meaning it does not really identify improper code. The updates should speak for themselves. They are given above.

ios alloc-release

My app is receiving memory warnings because it's asking for lot of memory. I try to release every allocation. However, sometimes I don't know how to do it.
For example: I have two pairs of .h and .m file. One of them makes connections with a server and the other with local SQLite.
Usually, the code which calls to a method from those files are like this:
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
return [local getRecentActivity];
}
getRecentActivity returns a NSMutableArray.
Well, in that piece of code we can see that I am allocating memory for the LocalStorageController but I never call to the release method so, I suppose, the more I call that function, the more memory I will be allocating.
If I call autorelease after init, it will crash.
Moreover, usually, I use this other kind of code:
ServerConnection *serv = [[ServerConnection alloc]init];
NSMutableArray list = [serv getMyListOfContacts];
Which uses ASIHTTPRequest and, if I call [serv release]; after the second line, the app crashes with EXC_BAD_ACCESS pointing to a line in ASIHTTPRequest library.
How is suppose to manage this situation?
Thank you very much!
The first case is easy;
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
NSMutableArray *tmp = [local getRecentActivity];
[local release];
return tmp;
}
The second case is hard to solve in a general way without seeing more of the actual code.
Using serv as a property would be fixing this retain/release problem.
In your .h:
#property (nonatomic, retain) ServerConnection *server;
In your .m:
#synthesize server;
- (void)dealloc {
[server release];
// The rest of your releases here...
[super dealloc];
}
- (void)yourMethod {
ServerConnection *myServConnection = [[ServerConnection alloc] init];
self.serv = myServConnection;
[myServConnection release];
NSMutableArray list = [self.serv getMyListOfContacts];
}
Just keep on using self.serv in this class from that point on and you won't have a problem with having the object being released.

What are the pros and cons of these different dealloc strategies?

I've seen several different approaches to memory management in iOS as regards releasing properties. After some debate with colleagues, the pros and cons have become muddled in my head.
I'm hoping to get a summary of pros and cons that will allow myself and others to easily choose a default approach while still understanding when to make exceptions. Here are the 3 variations I've seen:
Assume #property (nonatomic, retain) MyObject *foo;
// Release-only. Seems to be the favored approach in Apple's sample code.
- (void)dealloc {
[foo release];
[super dealloc];
}
// Property accessor set to nil.
- (void)dealloc {
self.foo = nil;
[super dealloc];
}
// Release, then nil.
- (void)dealloc {
[foo release];
foo = nil;
[super dealloc];
}
If you have a different variation to add, comment here and I'll edit the op.
Versions (1): is the best. Each of the others have attributes that may be harmful.
Version (2): It is generally advised not to use accessors in dealloc (or init). The reasoning behind this is that the object is in the process of being torn-down (or created) and is in an inconsistent state. This is especially true if you are writing a library where someone else may later override an accessor unaware that it may be called when the object is in an inconsistent state. (Of course even Apple sometimes breaks this rule -[UIView initWithFrame:] calls -[UIView setFrame:] if the argument is not CGRectZero which can make for fun debugging.
Version (3); Setting the ivar to nil serves no useful purpose, indeed it may mask an error and make debugging more difficult. To see why this is true, consider the following piece of code, assume myObject has a version (3) dealloc.
FastMovingTrain* train = [[FastMoving alloc] init];
MyObject* myObject = [[MyObject alloc] init];
myObject.foo = train;
[train release];
// my myObject.foo is the only thing retaining train
...
....
[myObject release];
// Because of version (3) dealloc if myObject
// points to the dealloced memory this line
// will silently fail...
[myObject.foo applyBrakes];
Interestingly enough this code provides an opportunity to demonstrate when setting a variable to nil after a release does make sense. The code can be made more resilient by modifying it as follows.
FastMovingTrain* train = [[FastMoving alloc] init];
MyObject* myObject = [[MyObject alloc] init];
myObject.foo = train;
[train release];
// my myObject.foo is the only thing retaining train
...
....
[myObject release];
myObject = nil;
// This assertion will fail.
NSAssert(myObject, #"myObject must not be nil");
[myObject.foo applyBrakes];
Just my $0.02.

Objective-C memory management issues

I am new to iPhone programming and Objective-C.
I am building a View-based application.
The problem is that none of the UIViewController's dealloc functions are ever called.
I have decided to unload all my retained objects programmaticaly, right before presenting the next UIViewController class.
I have resolved all the leaks detected by Xcode, tested the application with Leaks and Allocations Tools and everything seams OK, but the memory used builds up to around 180 MB and the app crashes.
The self.retainCount before presenting the next UIViewController is 1, the objects owned are released and the pointers are nil, but still the memory builds up.
Can you give me a clue? If needed I will post some code samples. Or can you send me some references to something like Objective-C memory management 101?
This is my interface:
#interface GameScreen : UIViewController
{
IBOutlet UILabel *timeLabel;
IBOutlet UIView *gameView;
IBOutlet UIImageView *gameViewFrame;
IBOutlet UIButton *showOutlineButton;
UIImage *puzzleImage;
UIView *optionsView;
UIView *blockView;
NSTimer *timer;
UIImageView *viewOriginalPicture;
SHKActivityIndicator *activityIndicator;
BOOL originalPictureShown;
BOOL outLineShown;
}
#property (nonatomic, retain) IBOutlet UILabel *timeLabel;
#property (nonatomic, retain) IBOutlet UIView *gameView;
#property (nonatomic, retain) IBOutlet UIImageView *gameViewFrame;
#property (nonatomic, retain) IBOutlet UIButton *showOutlineButton;
#property (nonatomic, retain) UIImage *puzzleImage;
And here is the implementation:
- (id) initWithPuzzleImage: (UIImage *) img
{
if((self = [super init]))
{
puzzleImage = [[UIImage alloc] init];
puzzleImage = img;
}
return self;
}
This is the function called when the user taps the exit button:
- (void) onExit
{
[timer invalidate];
CurrentMinuts = 0;
CurrentSeconds = 0;
//remove piece configurations
[pieceConfigMatrix removeAllObjects];
[pieceFramesMatrix removeAllObjects];
PuzzleViewController *modalView = [[PuzzleViewController alloc] init];
[self unloadObjects];
[self presentModalViewController:modalView animated:YES];
[modalView release];
}
And the unloadObjects function:
- (void) unloadObjects
{
[self resignFirstResponder];
[viewOriginalPicture release];
viewOriginalPicture = nil;
[timeLabel release];
timeLabel = nil;
[gameView release];
gameView = nil;
[originalImage release];
originalImage = nil;
[gameViewFrame release];
gameViewFrame = nil;
[timer release];
[showOutlineButton release];
showOutlineButton = nil;
}
I have a lead of what I do wrong, but I am not sure. Let me explain. I am adding the puzzle pieces to the 'gameView' property. For this, I have a 'SplitImage' object. The following function is called in - (void) viewDidLoad:
- (void) generatePuzzleWithImage:(UIImage *) image
{
SplitImage *splitSystem = [[SplitImage alloc] initWithImage:image andPuzzleSize:gPuzzleSize];
[splitSystem splitImageAndAddToView:self.gameView];
[splitSystem release];
}
Next, initialization function for the SplitImage class and the splitImageAndAddToView function:
- (id) initWithImage: (UIImage *) image andPuzzleSize: (int) pSize
{
if((self = [super init]))
{
UIImage *aux = [[[UIImage alloc] init] autorelease];
pieceCenterSize = [SplitImage puzzlePieceSizeForNumberOfPieces:pSize];
UIImage *outSideBallSample = [UIImage imageNamed:#"sampleHorizontal.jpg"]; //convexity size for puzzle size
outSideBallSample = [outSideBallSample resizedImageWithContentMode:UIViewContentModeScaleAspectFit bounds:CGSizeMake(pieceCenterSize, pieceCenterSize) interpolationQuality:kCGInterpolationHigh];
outSideBallSize = roundf(outSideBallSample.size.height);
puzzleSize = pieceCenterSize * pSize;
pieceNumber = pSize;
if(image.size.height < puzzleSize || image.size.height > puzzleSize || image.size.width < puzzleSize || image.size.width > puzzleSize)
{
aux = [SplitImage resizeImageForPuzzle:image withSize:puzzleSize];
aux = [SplitImage cropImageForPuzzle:aux withSize:puzzleSize];
}
aux = [aux imageWithAlpha];
originalImage = [[UIImage imageWithCGImage:aux.CGImage] retain];
mainImage = aux.CGImage;
imageSize = CGSizeMake(aux.size.width, aux.size.height);
NSLog(#"%#", NSStringFromCGSize(imageSize));
splitImageSize = CGSizeMake(pieceCenterSize + 2*outSideBallSize, pieceCenterSize+2*outSideBallSize);
}
return self;
}
- (void) splitImageAndAddToView: (UIView *) view
{
for (int i = 0; i < pieceNumber; i++)
for(int j = 0; j < pieceNumber; j++)
//some code
UIImage *mask;
mask = [self randomlyRetriveMaskWithPrefix:1 forPieceAtI:i andJ:j];
CGImageRef split = CGImageCreateWithImageInRect(mainImage, cuttingRect);
PuzzlePiece *splitView = [[PuzzlePiece alloc] initWithImage:[UIImage imageWithCGImage:split] andMask:mask centerSize:pieceCenterSize objectMatrixPosition:i*pieceNumber+j outSideBallSize:outSideBallSize pieceType:pieceType pieceFrame:cuttingRect];
[pieceFramesMatrix addObject:[NSValue valueWithCGRect:cuttingRect]];
[splitView setTag:(i+1)*100+j];
[view addSubview:splitView];
CGImageRelease(split);
[splitView release];
}
Thank you,
Andrei
Objective-C memory management 101 is here:
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
Ignore the stuff on garbage collection, it isn't available for iOS.
It's not normal to release objects before presenting the next UIViewController.
Assuming you have a NIB, its contents will be loaded the first time that you (or the system) accesses the view member. Your view controller will get a call to loadView, then subsequently to viewDidLoad. If you have any views you want to add programmatically, you can do that in loadView or viewDidLoad, if you want to interrogate objects loaded from the NIB then you can do that in viewDidLoad.
Once loaded, the view will remain in memory unless and until a low memory warning occurs. At that point it'll be released. You'll get viewDidUnload. You should release anything view related that you've still got an owning reference to in there, and set to nil any weak references you may have.
Any attempt to access the view property subsequently will cause the NIB to be reloaded, etc.
So, when presenting a new UIViewController, just present it. If you create objects that are used only for display of that controller then do so on viewDidLoad, and release them on viewDidUnload.
That all being said, the Leaks tool in Instruments should be able to tell you which types of object are leaking and where you first allocated them, so it makes finding leaks really quite easy. The only thing to watch out for is that if one object handles its properties/members entirely correctly but is itself leaked then anything it creates will generally also leak. So when something leaks, check the thing that created it isn't also leaking before tearing your hair out over why you can't find a problem.
First, retainCount is useless.
Activity Monitor is pretty close to just as useless. As well, the behavior in the simulator can be quite different on the memory use front. Better to focus debugging of a problem like this on the device.
Next, this:
The problem is that none of the
UIViewController's dealloc functions
are ever called. I have decided to
unload all my retained objects
programmaticaly, right before
presenting the next UIViewController
class.
Any time you find yourself programmatically working around incorrect behavior, you are just creating more bugs.
Step back from your custom hack and figure out why the dealloc isn't being called. Something somewhere is over-retaining the object. The allocations instrument with retain tracking turned on will show you exactly where all retains and releases are sent to the errant objects.
Leaks likely won't show anything if whatever is retaining the objects is still reachable from a global or the stack (i.e. leaks are objects that can never be used by your program again -- but there are many more ways to explode memory use without it being truly a leak).
You should also "Build and Analyze", then fix any problems it identifies.
Next, if you are seeing memory accretion of repeated operations on the user's part, then Heapshot analysis is extremely effective at figure out exactly what has gone wrong.
Some specific comments:
puzzleImage = [[UIImage alloc] init];
puzzleImage = img;
2 bugs; you are leaking a UIImage and not retaining img. That your app doesn't crash in light of the above code indicates that there is likely an over-retain elsewhere.
retainCount is not a reliable debugging tool.
You should never pay attention to or rely on retainCount. Just because you released it does not mean that some part of the program does not still have a reference to it. retainCount has no value.
Generally as a rule of thumb. If you use 'alloc' you must 'release' at some point.
Unless ofcourse you have put it into an autorelease pool.
Leaks should be able to point you to the objects that are leaking, using that, narrow down to where those objects are added, stored etc.
Post examples of your code on how you instantiate objects and where you release them, you maybe doing something wrong early on.
edit: apologies, i put 'init' not 'alloc' previously, thank you dreamlax, early morning mistake.

Resources