i have a big jpeg image that i want to load in tiles asynchronously in my opengl engine.
Everything works well if its done on the main thread but its slow.
When i try to put the tile loading on an NSOperationBlock, it always crashes when trying to access the shared image data pointer that i previously loaded on the main thread.
There must be something i don't get with background operation because i assume i can access memory sections that i created on the main thread.
What i try to do is the following :
#interface MyViewer
{
}
#property (atomic, assign) CGImageRef imageRef;
#property (atomic, assign) CGDataProviderRef dataProvider;
#property (atomic, assign) int loadedTextures;
#end
...
- (void) loadAllTiles:(NSData*) imgData
{
queue = [[NSOperationQueue alloc] init];
//Loop for Total Number of Textures
self.dataProvider = CGDataProviderCreateWithData(NULL,[imgData bytes],[imgData length],0);
self.imageRef = CGImageCreateWithJPEGDataProvider(self.dataProvider, NULL, NO, kCGRenderingIntentDefault);
for (int i=0; i<tileCount; i++)
{
// I also tried this but without luck
//CGImageRetain(self.imageRef);
//CGDataProviderRetain(self.dataProvider);
NSBlockOperation *partsLoading = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakpartsLoadingOp = partsLoading;
[partsLoading addExecutionBlock: ^ {
TamTexture2D& pTex2D = viewer->getTile(i);
CGImageRef subImgRef = CGImageCreateWithImageInRect(self.imageRef, CGRectMake(pTex2D.left, pTex2D.top, pTex2D.width, pTex2D.height));
//!!!Its crashing here!!!
CFDataRef cgSubImgDataRef = CGDataProviderCopyData(CGImageGetDataProvider(subImgRef));
CGImageRelease(subImgRef);
...
}];
//Adding Parts loading on low priority thread. Is it all right ????
[partsLoading setThreadPriority:0.0];
[queue addOperation:partsLoading];
}
I finally found out my problem...
I have read the Quartz2D doc and it seems that we should not use CGDataProviderCreateWithData and CGImageCreateWithJPEGDataProvider anymore. I guess there usage is not thread safe.
As suggested by the doc, i now use CGImageSource API like this :
self.imageSrcRef = CGImageSourceCreateWithData((__bridge CFDataRef)imgData, NULL);
// get imagePropertiesDictionary
CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(m_imageSrcRef,0, NULL);
self.imageRef = CGImageSourceCreateImageAtIndex(m_imageSrcRef, 0, imagePropertiesDictionary);
Related
I have an architecture where I have to call a local function to display an Image and then in the background need to upload the image to server so that once the Uploading is finished I can remove the local path used for displaying the image.
Functions
DidCmpletePickingImage() , DisplayImageUsingLocalPath() , UploadImageToServer() and RemoveImageFromLocal().
These are the activities. Now i have option to upload multiple images.
This is my current approach. I pick an array of images from and call The function to show them using local path.
for (NSInteger i = 0;i < photos.count ; i ++){
UIImage *img = photos[i];
img = [self imageWithImage:img scaledToWidth:400];
NSData *imageData = UIImageJPEGRepresentation(img, 0.40);
[self showLocally:imageData img:img];
}
After they are showed I start uploading them to server on background thread
-(void) showLocally:(NSData *)imageData img:(UIImage *)img{
// Code for showing it using temp path.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
[self fileUpload:img];
});
}
Then using the background thread File is uploaded to the server using AFNetwork and then after I get a response I call remove local file path.
But when I do this calling on Background all the images are being simultaneously calling fileUpload method and doing it concurrently which is increasing load on server. How can I block a call of the function till a the previous object which called the function is completely executed ?
You may want to have a look at using NSOperationQueue in conjunction with maxConcurrentOperationCount -- limiting the number of operations that can occur all at once.
Here's a small example of what I mean:
#import "ViewController.h"
#interface ViewController ()
#property (strong, nullable) NSOperationQueue *imageUploaderQ;
- (void)_randomOperationWithDelayOutputtingInteger:(NSInteger)intToOutput;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.imageUploaderQ = [[NSOperationQueue alloc] init];
// you can experiment with how many concurrent operations you want here
self.imageUploaderQ.maxConcurrentOperationCount = 3;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
for (NSInteger index = 0; index < 100; index++) {
[self.imageUploaderQ addOperationWithBlock:^{
[self _randomOperationWithDelayOutputtingInteger:index];
}];
}
}
- (void)_randomOperationWithDelayOutputtingInteger:(NSInteger)intToOutput {
// simulating taking some time to upload
// don't ever explicitly call sleep in your actual code
sleep(2);
NSLog(#"Integer output = %li", intToOutput);
}
#end
Here are links to Apple's documentation on
NSOperationQueue
maxConcurrentOperationCount
I have a class called Dictionary, where the init method looks like this:
- (id) init{
self = [super init];
if (self){
[self makeEmojiDictionaries];
}
return self;
}
- (void)makeEmojiDictionaries{
//next line triggers bad_exc_access error
self.englishEmojiAllDictionary = #{#"hi" : #"👋"}; //this is a strong, atomic property of NSDictionary
};
My issue is that the actual emoji dictionary is quite large, and I want to do all the heavy lifting in a non-main thread using GCD. However, whenever I get to the line where I set self.englishEmojiAllDictionary, I always get a bad_access error.
I am using GCD in the most normal way possible:
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
//Do long process activity
Dictionary *dictionary = [[Dictionary alloc] init];
});
Are there particular nuances to GCD or non-main thread work that I am missing? Any help is much appreciated - thank you!
Edit 1:
In case you'd like to try it yourself. I have uploaded a sample project that replicates this exception. My theory is that the NSDictionary I am initialization is simply too large.
I have moved your data from code to a plist file in the form:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>arancia meccanica</key><string>⏰🍊</string>
<key>uno freddo</key><string>🍺</string>
<key>un drink</key><string>🍸</string>
...
<key>bacio</key><string>💋</string>
<key>baci</key><string>💋👐</string>
</dict>
</plist>
(I took your data and used find-replace three times: ", => </string>, then ":#" => </key><string> and #" => <key>).
Then I have loaded the data using:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"dictionary"
ofType:#"plist"]
dictionary = [NSDictionary dictionaryWithContentsOfFile:filePath];
That has fixed the problem. Note that you should never hardcode your data into source code.
The exact reason for the bug was pretty hard to pinpoint. The NSDictionary literal uses method +[NSDictionary dictionaryWithObjects:forKeys:count:].
My assembler knowledge is very poor but I think that before calling this initializer, all the keys & values are put on the stack.
However, there is a difference between the stack size of the main thread and the stack size of the background thread (see Creating Threads in Thread Programming Guide).
That's why the issue can be seen when executing the code on the background thread. If you had more data, the issue would probably appear on the main thread too.
The difference between stack size on main thread and background thread can be also demonstrated by the following simple code:
- (void)makeEmojiDictionaries {
// allocate a lot of data on the stack
// (approximately the number of pointers we need for our dictionary keys & values)
id pointersOnStack[32500 * 2];
NSLog(#"%i", sizeof(pointersOnStack));
}
First of all, I suggest you use a file (plist, txt, xml, ...) to store large data, then read it at runtime, or download it from a remote server.
For your issue, it is because of the limitation of stack size. On iOS, the default stack size for the main thread is 1 MB, and 512 KB for the secondary threads. You can check it out via [NSThread currentThread].stackSize.
Your hardcoded dictionary costs almost 1 MB of stack, that is why your app will be crash on a secondary thread, but be OK on the main thread.
If you want to do this work on a background thread, you must increase the stack size for that thread.
For example:
// NSThread way:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(populateDictionaries) object:nil];
thread.stackSize = 1024*1024;
[thread start];
Or
// POSIX way:
#include <pthread.h>
static void *posixThreadFunc(void *arg) {
Dictionary *emojiDictionary = [[Dictionary alloc] init];
return NULL;
}
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
pthread_t posixThread;
pthread_attr_t stackSizeAttribute;
size_t stackSize = 0;
pthread_attr_init (&stackSizeAttribute);
pthread_attr_getstacksize(&stackSizeAttribute, &stackSize);
if (stackSize < 1024*1024) {
pthread_attr_setstacksize (&stackSizeAttribute, REQUIRED_STACK_SIZE);
}
pthread_create(&posixThread, &stackSizeAttribute, &posixThreadFunc, NULL);
}
#end
Or
// Create mutable dictionary to prevent stack from overflowing
- (void)makeEmojiDictionaries {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[#"arancia meccanica"] = #"⏰🍊";
dict[#"uno freddo"] = #"🍺";
dict[#"un drink"] = #"🍸";
.....
self.englishEmojiAllDictionary = [dict copy];
}
FYI:
Thread Costs
Customizing Process Stack Size
The correct pattern when you need to do something slow is to do the work privately on a background queue, and then dispatch back to the main queue to make the completed work available to the rest of the app. In this case, you don't need to create your own queue. You can use one of the global background queues.
#import "ViewController.h"
#import "Dictionary.h"
#interface ViewController ()
#property (nonatomic, strong, readonly) Dictionary *dictionary;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self updateViews];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
Dictionary *dictionary = [[Dictionary alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
_dictionary = dictionary;
[self updateViews];
});
});
}
- (void)updateViews {
if (self.dictionary == nil) {
// show an activity indicator or something
} else {
// show UI using self.dictionary
}
}
#end
Loading the dictionary from a file is a good idea, and you can do that in the background queue and then dispatch back to the main thread with the loaded dictionary.
I'm trying to optimize some image filtering by using a singleton object that contains the image filter for me to use anywhere. I am running into an issue where I believe I am calling the filter several times at once (several cells are displayed, after downloading image, before display, I am processing the image with a filter, which may occur pretty much at the same time).
I run into this issue:
NSAssert(framebufferReferenceCount > 0, #"Tried to overrelease a framebuffer, did you forget to call -useNextFrameForImageCapture before using -imageFromCurrentFramebuffer?");
https://github.com/BradLarson/GPUImage/blob/master/framework/Source/GPUImageFramebuffer.m#L269
Here's my code:
Interface:
#import <GPUImage/GPUImage.h>
#interface GPUImageFilterManager : NSObject
+ (GPUImageFilterManager*)sharedInstance;
#property (nonatomic, strong) GPUImageiOSBlurFilter *blurFilter;
#property (nonatomic, strong) GPUImageLuminanceThresholdFilter *luminanceFilter;
#end
Implementation:
#import "GPUImageFilterManager.h"
#implementation GPUImageFilterManager
+ (GPUImageFilterManager*)sharedInstance {
static GPUImageFilterManager *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [GPUImageFilterManager new];
});
return _sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
self.luminanceFilter = [[GPUImageLuminanceThresholdFilter alloc] init];
self.luminanceFilter.threshold = 0.5;
self.blurFilter = [[GPUImageiOSBlurFilter alloc] init];
[self.blurFilter setBlurRadiusInPixels:BLUR_RADIUS];
[self.blurFilter setSaturation:SATURATION];
[self.blurFilter setDownsampling:DOWNSAMPLING];
[self.blurFilter setRangeReductionFactor:RANGEREDUCTION];
}
return self;
}
-(GPUImageiOSBlurFilter*)blurFilter {
[_blurFilter useNextFrameForImageCapture];
return _blurFilter;
}
-(GPUImageLuminanceThresholdFilter*)luminanceFilter {
[_luminanceFilter useNextFrameForImageCapture];
return _luminanceFilter;
}
And in my code I call:
[[[GPUImageFilterManager sharedInstance] blurFilter] imageByFilteringImage:image];
Previously, I had a GPUImageLuminanceThresholdFilter property in each cell and I wanted to optimize this to just use a single instance of it, but it now seems I won't be able to process multiple images at once. Any ideas or advice here?
My guess about what's going wrong here is that -imageByFilteringImage: isn't really an atomic operation, and if you're triggering it on multiple threads at the same time, you're going to get bizarre behavior. Take a look at what happens inside that method (via the -newCGImageByFilteringCGImage: method):
- (CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter;
{
GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithCGImage:imageToFilter];
[self useNextFrameForImageCapture];
[stillImageSource addTarget:(id<GPUImageInput>)self];
[stillImageSource processImage];
CGImageRef processedImage = [self newCGImageFromCurrentlyProcessedOutput];
[stillImageSource removeTarget:(id<GPUImageInput>)self];
return processedImage;
}
A GPUImagePicture is created, attached to the filter, processed, and the output extracted. If you're using the same filter across multiple threads, and you call this method, odds are that you're going to interrupt this at various points and create bizarre filtering pathways. Thus the error above (as well as other potential crashes and image corruption).
If you want to use this filter in a singleton, my recommendation would be to set up a serial dispatch queue and wrap accessed to that filter in dispatches to that queue. In particular, I'd wrap synchronous dispatches to this queue around your -imageByFilteringImage: above to guarantee that this always fully executes and returns an image before being triggered again by a different image and thread.
The GPUImage OpenGL ES context can only perform a single rendering operation at a time, so you don't lose much by limiting access to the filter to only one thread at a time. It's also pretty simple to implement, and I use this pattern of limiting access to shared resources via GCD queues throughout the framework.
I have custom UICollectionViewCell subclass where I draw with clipping, stroking and transparency. It works pretty well on Simulator and iPhone 5, but on older devices there is noticeable performance problems.
So I want to move time-consuming drawing to background thread. Since -drawRect method is always called on the main thread, I ended up saving drawn context to CGImage (original question contained code with using CGLayer, but it is sort of obsolete as Matt Long pointed out).
Here is my implementation of drawRect method inside this class:
-(void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
if (self.renderedSymbol != nil) {
CGContextDrawImage(ctx, self.bounds, self.renderedSymbol);
}
}
Rendering method that defines this renderedSymbol property:
- (void) renderCurrentSymbol {
[self.queue addOperationWithBlock:^{
// creating custom context to draw there (contexts are not thread safe)
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(nil, self.bounds.size.width, self.bounds.size.height, 8, self.bounds.size.width * (CGColorSpaceGetNumberOfComponents(space) + 1), space, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(space);
// custom drawing goes here using 'ctx' context
// then saving context as CGImageRef to property that will be used in drawRect
self.renderedSymbol = CGBitmapContextCreateImage(ctx);
// asking main thread to update UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self setNeedsDisplayInRect:self.bounds];
}];
CGContextRelease(ctx);
}];
}
This setup works perfectly on main thread, but when I wrap it with NSOperationQueue or GCD, I'm getting lots of different "invalid context 0x0" errors. App doesn't crash itself, but drawing doesn't happen. I suppose there is a problem with releasing custom created CGContextRef, but I don't know what to do about it.
Here's my property declarations. (I tried using atomic versions, but that didn't help)
#property (nonatomic) CGImageRef renderedSymbol;
#property (nonatomic, strong) NSOperationQueue *queue;
#property (nonatomic, strong) NSString *symbol; // used in custom drawing
Custom setters / getters for properties:
-(NSOperationQueue *)queue {
if (!_queue) {
_queue = [[NSOperationQueue alloc] init];
_queue.name = #"Background Rendering";
}
return _queue;
}
-(void)setSymbol:(NSString *)symbol {
_symbol = symbol;
self.renderedSymbol = nil;
[self setNeedsDisplayInRect:self.bounds];
}
-(CGImageRef) renderedSymbol {
if (_renderedSymbol == nil) {
[self renderCurrentSymbol];
}
return _renderedSymbol;
}
What can I do?
Did you notice the document on CGLayer you're referencing hasn't been updated since 2006? The assumption you've made that CGLayer is the right solution is incorrect. Apple has all but abandoned this technology and you probably should too: http://iosptl.com/posts/cglayer-no-longer-recommended/ Use Core Animation.
Issue solved by using amazing third party library by Mind Snacks — MSCachedAsyncViewDrawing.
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.