Please see attached image for reference. All of the viewControllers have been already removed from the app but Memory Debugger shows its instances as well as all its properties. When i click the Show Only Leaked blocks filter of Memory Debugger, the viewControllers and other instances do not appear in it. Does it mean there are no leaks?
How do I solve the problem. What does it mean?
I do have the PKYStepper block in the CartViewController's cellForRowAtIndexPath (Stepper is a UIControl in my TableViewCell) method as follows :
PKYStepper *qtyStepper = [cell viewWithTag:993];
qtyStepper.tappedCallback = ^(PKYStepper *stepper) {
NSLog(#"Tapped!");
rowSelected = indexPath;
if (((Dish*)((MenuSubSection*)_section.subSections[0]).dishesArray[indexPath.row]).disheOptions.count)
{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
NSBundle* bun = [NSBundle bundleWithIdentifier:#"com.test.test"];
DishItemOption *dishOptions = [[bun loadNibNamed:#"DishItemOption" owner:self options:nil] objectAtIndex:0];
dishOptions.frame = CGRectMake(0, 0, window.frame.size.width, window.frame.size.height);
dishOptions.dish = [[Dish alloc] initWithDishObject:((Dish*)((MenuSubSection*)_section.subSections[0]).dishesArray[indexPath.row])];
dishOptions.delegate = self;
[window addSubview:dishOptions];
}
};
How to make it reference the Weak Self?
It looks like you've likely captured the view controller in a callback block of some sort.
Specifically, PKYStepper seems to have a callback block that is strongly referencing the view controller. Either make sure said reference is weak or make sure the block is properly destroyed when the view controller is torn down.
Found the solution. Updated my callbacks to the following :
__weak typeof(self) weakSelf = self;
qtyStepper.incrementCallback = ^(PKYStepper *stepper, float newValue) {
CartViewController *sSelf = weakSelf;
[sSelf updateTotalCharges]; //Had to use WEAKSELF in the callback!
};
Related
I am going through Big Nerd Ranch iOS textbook, 19th chapter and it is unclear for me why they are doing reassignment of pointers there to avoid strong reference cycle. Basically they have a block of code, actionBlock that has a strong reference to the class, BNRItemCell, and the reference persists beyond the life of the block. BNRItemCell is referencing the block through #property: #property (strong, nonatomic) void (^actionBlock)(void). Obviously that creates strong reference cycle. To avoid it they define a __weak BNRItemCell *weakCell = cell outside of the block, and then BNRItemCell *strongCell = weakCell inside the block. They say that strongCell should persist while the block is executing and that it will be destroyed when the block is finished. I do not understand the reassignment within the block, BNRItemCell *strongCell = weakCell, and what is the point of the pointer being __weak. If, say, we do just BNRItemCell *strongCell = cell, where cell is the strong reference to the class, shouldn't it be OK? strongCell would still be destroyed when the block has finished executing.
I tried to visualize what is going on and what they are doing does not make sense for me. To make things clear, what is the difference between
__weak someClass *weakPointer = strongPointer_1;
someObject.actionBlock = ^{
someClass *strongPointer_2 = weakPoiter;
// Here we are using strongPointer_2
}
and
someObject.actionBlock = ^{
someClass *strongPointer_2 = strongPointer_1;
// Here we are using strongPointer_2
}
?
I just can not get the difference, so I guess, I do not have clear understanding of what is going on behind the scenes.
The full code is below:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Get a new or recycled cell
BNRItemCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"BNRItemCell"
forIndexPath:indexPath];
// Set the text on the cell with the description of the item
// that is the nth index of items, where n = row this cell
// will appear in on the tableview
NSArray *items = [[BNRItemStore sharedStore] allItems];
BNRItem *item = items[indexPath.row];
// Configure the cell with the BNRItem
cell.nameLabel.text = item.itemName;
cell.serialNumberLabel.text = item.serialNumber;
cell.valueLabel.text = [NSString stringWithFormat:#"$%d", item.valueInDollars];
cell.thumbnailView.image = item.thumbnail;
__weak BNRItemCell *weakCell = cell;
cell.actionBlock = ^{
NSLog(#"Going to show image for %#", item);
BNRItemCell *strongCell = weakCell;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad){
NSString *itemKey = item.itemKey;
// if there is no image, we don't need to display anything
UIImage *img = [[BNRImageStore sharedStore] imageForKey:itemKey];
if (!img) {
return;
}
BNRImageViewController *ivc = [[BNRImageViewController alloc] init];
ivc.image = img;
ivc.modalPresentationStyle = UIModalPresentationPopover;
ivc.preferredContentSize = CGSizeMake(380, 300);
CGRect frame = [self.view convertRect:strongCell.thumbnailView.bounds
toView:self.view];
// frame.origin.y -= 150;
UIPopoverPresentationController *popoverController = ivc.popoverPresentationController;
popoverController.permittedArrowDirections = UIPopoverArrowDirectionUp;
popoverController.sourceView = cell.thumbnailView;
popoverController.sourceRect = frame;
[self.navigationController presentViewController:ivc animated:YES completion:nil];
}
};
return cell;
}
It's important to understand why the "normal" case of capturing self or some other reference in a block causes a retain cycle.
If you have an instance variable holding a strong reference to a block, that block won't be dealloc'd until that reference is niled. If the block captures a strong reference to the instance which references the block, that instance won't be dealloced until that reference is niled. If both the instance and the block hold these references at the same time, you have your classic loop, unless one explicitly nils its reference to the other.
The key thing here is that the block's reference to the class instance is held in the capture data. This data sticks around as long as the block does.
Now we can see why capturing a weak pointer will help. If the problem is the capture data, and not the references in the block itself, a weak pointer takes care of our cycle rather nicely. The class instance can now be dealloced independently of the block, because the block has no strong reference.
Now the question is, why have a strong reference within the body of the block itself? This is simply to make sure the instance won't be dealloced while the block is executing. This isn't a real concern for stuff that is all running on the main thread, but that block might be dispatching to a background thread.
Why doesn't this (re)create a retain cycle? Well, it does, but only temporarily. The strong reference inside the block captures the weak pointer, which can be niled. The strong reference is a block-scoped variable that will be cleaned up when the block exits. ARC ensures that such references are released before the return from the block, thereby stopping the cycle.
If I'm understanding this correctly, it looks like they are trying to prevent a retain cycle.
__weak BNRItemCell *weakCell = cell;
cell.actionBlock = ^{
BNRItemCell *strongCell = weakCell; // weakCell is captured here at block declaration time
}
Blocks capture and retain variable values, so if it captured a strong BNRItemCell (reference to itself), its strongly retained actionBlock would create a retain cycle. By capturing the BNRItemCell as weak, if the table view ever decided to discard the cell, your cell will be properly deallocated.
If it helps you, think about how many strong references exist to the BNRItemCell. The table view keeps a strong reference (+1) while the cell is on screen. By capturing a strong reference (+1) in the actionBlock, you'd have brought its reference count up to 2.
An object will remain in memory as long as its reference count is 1 or more. So even if the UITableView releases its reference to the cell, you've still got the reference in its actionBlock, which will keep its reference count positive. Since you never set actionBlock = nil (which you shouldn't), actionBlock will never get released, and therefore the BNRItemCell never will either.
Note that weakCell is the only captured value at block creation time, strongCell won't even exist until the block is actually called.
TL;DR: The cell would be keeping a strong reference to itself through its actionBlock, which would create a retain cycle.
Assuming that a view controller is created like this:
#property (nonatomic, strong) SomeViewController *someViewController;
...
self.someViewController = [[SomeViewController alloc] initWithView:imgView];
[self addChildViewController:self.someViewController];
self.someViewController.view.frame = self.view.bounds;
[self.mainView addSubview:self.someViewController.view];
Why would it not get released by the following?
__weak MainViewController *weakSelf = self;
self.someViewController.didCloseBlock = ^{
[weakSelf.someViewController.view removeFromSuperview];
[weakSelf.someViewController willMoveToParentViewController:nil];
[weakSelf.someViewController removeFromParentViewController];
weakSelf.someViewController = nil;
};
I can tell it's not getting released because if I keep opening and closing the view controller (creating a new instance each time I open one), it causes low memory warnings (and then a crash on iOS5), and in SomeViewController didReceiveMemoryWarning, I see a log for the number of times I've created a new SomeViewController. For example, when I get the memory warning after opening 9 new SomeViewControllers, I will get 9 didReceiveMemoryWarning logs, indicating that I have 9 SomeViewController instances in memory, even though I'm nilling each one out in the code above.
You're retaining your view once in your property with the strong annotation and again with self.someViewController = [[SomeViewController alloc] initWithView:imgView];
Using the synthesized variable should get rid of this:
_someViewController = [[SomeViewController alloc] initWithView:imgView];
If you're not using ARC, you can use self.someViewController = [[[SomeViewController alloc] initWithView:imgView] autorelease];
I'd probably go for the first option, ARC or not though.
You are just setting the block didCloseBlock, nothing else actually. Do you execute it?
I am trying to use the iOS zxing Widget for QR Code Scanning. I have a ViewController which is pushed as an Item in my UINavigationController or presented Modally from another ViewController. This ViewController has a SegmentedControl for 3 different views. Two of those Views are UIWebViews which load simple Websites, nothing special about them.
The selection looks something like this:
- (IBAction)segmentedControlValueChanged:(id)sender {
NSString *urlString;
ZXingWidgetController *widController;
QRCodeReader* qrcodeReader;
NSSet *readers;
switch (segmentedControl.selectedSegmentIndex) {
case 0:
[self.view bringSubviewToFront:self.productSearchWebView];
urlString = [[SACommunicationService sharedCommunicationService] getURLforKey:kURLTypeProductSearch];
[self.productSearchWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
break;
case 1:
[self.view bringSubviewToFront:self.marketSearchWebView];
urlString = [[SACommunicationService sharedCommunicationService] getURLforKey:kURLTypeMarketSearch];
[self.marketSearchWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
break;
case 2:
widController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
qrcodeReader = [[QRCodeReader alloc] init];
readers = [[NSSet alloc] initWithObjects:qrcodeReader,nil];
widController.readers = readers;
[self.QRCodeScannerView addSubview:widController.view];
[self.view bringSubviewToFront:self.QRCodeScannerView];
break;
default:
break;
}
}
I tried to debug and go step by step and find out where the problem comes from:
Decoder (which is part of the underlying ZXing logic) tries to call "failedToDecodeImage:" from its delegate (which should be the ZXingWidgetController class) and crashes (EXC_BAD_ACCESS)
While stepping through I found out that the "cancelled" Method of the ZXingWidgetController gets called. Now I don't really know why this method gets called. The Widget shouldn't stop right after initializing and starting the decoder.
So the answer is a very simple one.
I was using iOS 5.0 and ARC. The ZXing ViewController is instantiated locally inside the method. Since the ViewController itself does not get viewed ARC sets a release at the end of the method and the ViewController gets freed. Since the ViewController is released, the view which was retained by the ViewController will be released as well. Cancelled is called because the Main ViewController does not exist anymore and calling some method on a nil pointer results in a BAD_ACCESS.
The Solution here was to set the ZXingViewController as a global strong property. This kept the object from being released right at the end of that method and thus the view which was added as a subview to another ViewControllers view was held in memory as long as the ViewController was alive.
You're not supposed to add controller views as subviews of another view. You're suposed to present controllers using the various UIViewController mechanisms.
By doing what you're doing, you're violating UIViewController contracts. The widget isn't getting things like viewWillAppear, viewDidAppear, etc.
If you want to use ZXing at the UIView/CALayer level instead of the UIViewController level, look at the classes in the ZXing objc directory.
Try this... also in the .h file make this ZXingWidgetController *widController;
And also to the viewScanner set clipToBounds to true.
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(openScanner) withObject:nil afterDelay:0.5];
}
-(void)openScanner
{
self.widController = [[ZXingWidgetController alloc] initMiniWithDelegate:self showCancel:NO OneDMode:YES];
NSMutableSet *readers = [[NSMutableSet alloc ] init];
MultiFormatReader* reader = [[MultiFormatReader alloc] init];
[readers addObject:reader];
self.widController.readers = readers;
[viewScanner addSubview:self.widController.view];
}
I'm developing a large scale application for iOS 5 using ARC in xcode. The system seems to work well except for when I'm trying to deallocate one of my interfaces. I'm using a framework called WhirlyGlobe to create a 3D interactive globe in the first view controller.
When I switch view controllers (between the 4 I have), I notice that the memory being used for the view controller with the globe isn't being released. All the other view controllers (only using simple views and images) release their memory fine - But the globe stays resident, or so it seems. When navigating back to the globe, I get almost a 10mb jump in memory due to 1mb allocations in "glsmLoadTextureLevelBuffer".
To get on with my question - Is there anything more I can do, with ARC active, to help release my objects? I've noticed my viewDidUnload and dealloc methods are not being called at all, and that the only way I can get anything to fire is using viewDidDisappear (which is not ideal obviously) - See below:
- (void)clear
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (self.layerThread)
{
[self.layerThread cancel];
while (!self.layerThread.isFinished)
[NSThread sleepForTimeInterval:0.001];
}
self.glView = nil;
self.sceneRenderer = nil;
if (theScene)
{
delete theScene;
theScene = NULL;
}
self.theView = nil;
self.texGroup = nil;
self.layerThread = nil;
self.earthLayer = nil;
self.vectorLayer = nil;
self.labelLayer = nil;
self.interactLayer = nil;
self.pinchDelegate = nil;
self.panDelegate = nil;
self.tapDelegate = nil;
self.longPressDelegate = nil;
self.rotateDelegate = nil;
}
- (void)viewDidDisappear:(BOOL)animated {
NSLog(#"dealloc - viewDidDisappear");
[self clear];
}
I'm setting everything I no longer need to nil. Is this the best practise?
The globe setup code:
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
// Set up an OpenGL ES view and renderer
EAGLView *ev = [[EAGLView alloc] initWithFrame:CGRectMake(0, 0, 824, self.view.frame.size.height)];
self.glView = ev;
self.sceneRenderer = [[SceneRendererES1 alloc] init];
UIColor *whiteC = [UIColor whiteColor];
[sceneRenderer setClearColor:whiteC];
glView.renderer = sceneRenderer;
glView.frameInterval = 2; // 60 fps (2)
[self.view addSubview:glView];
self.view.backgroundColor = [UIColor blackColor];
self.view.opaque = YES;
self.view.autoresizesSubviews = YES;
//glView.frame = self.view.bounds;
glView.frame = CGRectMake(275, GLOBE_HEIGHT_FIX, 768, SCREEN_HEIGHT+STATUS_BAR_HEIGHT); // was 260 x
glView.backgroundColor = [UIColor whiteColor];
self.view.backgroundColor = [UIColor whiteColor]; // red for debug
// Create the textures and geometry, but in the right GL context
[sceneRenderer useContext];
self.texGroup = [[TextureGroup alloc] initWithInfo:[[NSBundle mainBundle] pathForResource:#"bdGlobe_info" ofType:#"plist"]];
// Need an empty scene and view
theScene = new WhirlyGlobe::GlobeScene(4*texGroup.numX,4*texGroup.numY);
self.theView = [[WhirlyGlobeView alloc] init];
[theView setFarPlane:5.0];
[theView setHeightAboveGlobe:GLOBE_HEIGHT_VIEW];
if (globeShouldAnimate) glView.alpha = 1.0;
// Need a layer thread to manage the layers
self.layerThread = [[WhirlyGlobeLayerThread alloc] initWithScene:theScene];
// Earth layer on the bottom
self.earthLayer = [[SphericalEarthLayer alloc] initWithTexGroup:texGroup];
[self.layerThread addLayer:earthLayer];
// Set up the vector layer where all our outlines will go
self.vectorLayer = [[VectorLayer alloc] init];
[self.layerThread addLayer:vectorLayer];
// General purpose label layer.
self.labelLayer = [[LabelLayer alloc] init];
[self.layerThread addLayer:labelLayer];
self.interactLayer = [[InteractionLayer alloc] initWithVectorLayer:self.vectorLayer labelLayer:labelLayer globeView:self.theView
countryShape:[[NSBundle mainBundle] pathForResource:#"10m_admin_0_map_subunits" ofType:#"shp"]
oceanShape:[[NSBundle mainBundle] pathForResource:#"10m_geography_marine_polys" ofType:#"shp"]
regionShape:[[NSBundle mainBundle] pathForResource:#"10m_admin_1_states_provinces_shp" ofType:#"shp"]];
self.interactLayer.maxEdgeLen = [self.earthLayer smallestTesselation]/10.0;
[self.layerThread addLayer:interactLayer];
// Give the renderer what it needs
sceneRenderer.scene = theScene;
sceneRenderer.view = theView;
// Wire up the gesture recognizers
self.panDelegate = [PanDelegateFixed panDelegateForView:glView globeView:theView];
self.tapDelegate = [WhirlyGlobeTapDelegate tapDelegateForView:glView globeView:theView];
self.longPressDelegate = [WhirlyGlobeLongPressDelegate longPressDelegateForView:glView globeView:theView];
// Kick off the layer thread
// This will start loading things
[self.layerThread start];
You can use the allocations instrument for this. Using heap shot, you can mark the heap at various points in the lifetime of your application and compare the object graph that constitutes the current allocations in memory at the point of each snapshot. That should help you narrow down what's being retained and by whom.
This is anecdotal, but COULD be a similar situation.
I just had a situation where my objects were not getting released ever, in ARC, even though they were accessed in a static way (e.g., [SingletonThing instance].thing) because the autorelease pool wasn't draining. The reason for this was a bizarre endless loop that runs on its own thread. Putting a separate #autorelease block for the enclosing code.
On the other hand, even if one of your (or the libs) object has the UIView as a subview, I think your UIViewController will never viewDidUnload and therefore never dealloc. I have to check this empirically.
make sure your -(void)viewDidDisappear:(BOOL)animated is invoked.
if you use
[self.view addSubview:yourViewController.view];
and
[yourViewController.view removeFromSuperview];
then viewDidDisappear: and viewDidAppear: will not be invoked
these callback will only be invoked
when you use presentViewController: in IOS 5
or presentModalViewController:
and dismissViewControllerAnimated: in IOS 5
or dismissModalViewControllerAnimated:
or use UINavigationController to present and dismiss your viewController
I found the problem after using heap shots (thanks to Mark Adams) - Turns out I wasn't making a couple of delegates weak entities, so they weren't being released when changing the view controller. Default strong delegates stay resident.
Thanks to all the suggestions, they all helped point me in the right direction :)
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.