I have an image processing application that I am using to test potential speedup of blocks/threads on iOS. The algorithm works fine, uses a fair amount of memory and then dumps it after running.
I made a test suite that runs the algorithm 10 times sequentially and 10 times using my parallel implementation. If I try to run it on a phone it crashes due to memory pressure - it ends up needing about 432MB of memory. However, once the suite is done it all finally gets cleaned up :/
Each individual run is using around 25MB of memory. So I thought the solution would be to reset all of my objects after each run and they would get cleaned up. I basically have 2 processing objects that do all of my work. So after each run I thought setting them to nil would cause them to be recreated and the old versions to be destroyed and that memory freed. However, it had no effect on my memory usage.
Is there something more I need to do to free up the memory in-between calls? I thought Objective-C was now using reference counting, and once I eliminate the only reference - in my viewController - that it would be freed. Any suggestions would be greatly appreciated.
This is the test suite algorithm with my attempted memory-freeing:
- (void)runTestSuite
{
// Sequential
NSLog(#"Sequential Run: ");
for (int i = 0; i < 10; i++) {
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:NO];
}
[self printResults];
[self clearResults];
// Parallel
NSLog(#"Parallel Run: ");
for (int i = 0; i < 10; i++) {
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:YES];
}
[self printResults];
[self clearResults];
}
Other than working on the algorithm to improve memory usage you could give #autoreleasepool a shot. This will free your freeable objects used in between every loop, without the need of the current loop cycle to end.
for (int i = 0; i < 10; i++) {
#autoreleasepool {
}
}
From the documentation:
In many situations, allowing temporary objects to accumulate until the
end of the current event-loop iteration does not result in excessive
overhead; in some situations, however, you may create a large number
of temporary objects that add substantially to memory footprint and
that you want to dispose of more quickly. In these latter cases, you
can create your own autorelease pool block. At the end of the block,
the temporary objects are released, which typically results in their
deallocation thereby reducing the program’s memory footprint
Some source code would help. In absence of that, a general suggestion: wrap the code that is doing the image process in an autoreleasepool (see this Apple document).
This will discard temporary objects as soon as possible, reducing memory spike.
Autorelease pool should be used here
Many programs create temporary objects that are autoreleased. These
objects add to the program’s memory footprint until the end of the
block. In many situations, allowing temporary objects to accumulate
until the end of the current event-loop iteration does not result in
excessive overhead; in some situations, however, you may create a
large number of temporary objects that add substantially to memory
footprint and that you want to dispose of more quickly. In these
latter cases, you can create your own autorelease pool block. At the
end of the block, the temporary objects are released, which typically
results in their deallocation thereby reducing the program’s memory
footprint
- (void)runTestSuite
{
// Sequential
NSLog(#"Sequential Run: ");
for (int i = 0; i < 10; i++) {
#autoreleasepool
{
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:NO];
}
}
[self printResults];
[self clearResults];
// Parallel
NSLog(#"Parallel Run: ");
for (int i = 0; i < 10; i++) {
#autoreleasepool
{
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:YES];
}
}
[self printResults];
[self clearResults];
}
I have an array of images that changes to a random image with an IBAction attached to a button when pressed. When run on a simulator it runs fine but on a device it seems to crash after memory warnings. It also lags when the button is pressed. I want it to run smoothly, and not crash. I believe this has something to do with my array not releasing each image. Here is my array inside my buttons code.
-(IBAction)buttonPressed:(id)sender;
{
int ptr = arc4random() % 132;
NSArray* images = [[NSArray alloc] initWithObjects:#"17",#"29",#"55",#"400",#"Alcohol",#"Arianny",#"Anderson",#"Approach",#"Arab",#"Asian",#"Attitude",#"Attraction",#"Beckinsale",#"Blueberry",#"Brain",#"Break",#"Breakups",#"Burns",#"Buttocks",#"Charity",#"Check",#"Chicago",#"Chocolate",#"Coco",#"Coffee",#"College",#"Commit",#"Common",#"Confident",#"Cook",#"Count",#"Country",#"Couples",#"Courtship",#"Criminal",#"Cruz",#"Date",#"Date14",#"Deed",#"Degree",#"Dropped",#"Dushku",#"Dworaczyk",#"Eating",#"Emotion",#"Exercise",#"Fwb",#"Fantasies",#"Fitness",#"Flings",#"Flirt",#"Foot",#"Forget",#"Friendship",#"Frowning",#"Hum",#"Impression",#"Hair",#"Happiness",#"Hazel",#"Headache",#"Instant",#"Interest",#"Internet",#"Jacobs",#"January",#"Jimena",#"Jolie",#"Kalia",#"Kardashian",#"Kiss",#"Kissing",#"Krupa",#"Larissa",#"Latino",#"Laughter",#"Lip",#"London",#"Love",#"Love2",#"Love3",#"Love4",#"Math",#"Maximus",#"Melany",#"Memory",#"Men",#"Milian",#"Miller",#"Millions",#"Mind",#"Monica",#"Muscle",#"Partner",#"Naps",#"Negativity",#"Novels",#"Oral",#"Ossa",#"Pain",#"Positions",#"Productive",#"Proximity",#"Read",#"Reputation",#"Second",#"Sensitive",#"Serious",#"Shaking",#"Sleep2",#"Smile",#"Smoke",#"Smoke2",#"Smokers",#"Sneeze",#"Socks",#"Sold",#"Spot",#"Stimuli",#"Stone",#"Survey",#"Swell",#"Tattoo",#"Teacher",#"Teeth",#"Vickers",#"Violence",#"Wallet",#"Weight",#"Windmills.png",#"White",#"Women",#"Yawn",nil];
[imageView setImage:[UIImage imageNamed:[images objectAtIndex:ptr]]];
ptr++;
NSLog(#"button pressed");
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.images=nil;
}
- (void)dealloc {
[images release];
[adView release];
[super dealloc];
}
As I see your code is not running with ARC so when you create images array it is not deleted from memory until you call release. write release after you don't need your array anymore:
int ptr = arc4random() % 132;
NSArray* images = [[NSArray alloc] initWithObjects:#"17",#"29"];
[imageView setImage:[UIImage imageNamed:[images objectAtIndex:ptr]]];
ptr++;
[images release];
First off, use ARC, if you can.
You have 2 things leaking memory: the image and the array of image names. Since the image names are constant, you only need to create this array once.
Create ivars for the image and for the image name array:
UIImage *_image;
NSArray *_imageNames; // init this in your viewDidLoad:
Then, in your button press handler:
-(IBAction)buttonPressed:(id)sender;
{
int ptr = arc4random() % 132;
[_image release];
_image = UIImage imageNamed:_images[ptr]];
[imageView setImage:_image];
ptr++;
NSLog(#"button pressed");
}
Finally, release _imageNames:
- (void)dealloc
{
[_imageNames release];
// release everything else.
}
Again, you should really consider switching to ARC. You'll be glad you did.
You actually have two problems here, both surrounding this line:
NSArray* images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
The first is that the NSArray* at the beginning of the line declares a new local variable, images. This is separate from the property self.images that you try to erase in -viewDidUnload and release in -dealloc. Removing the NSArray* from the line will fix this issue, storing the array into the self.images property as you seem to intend.
That gives you a line like this:
images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
The second problem is that you re-create the images array each time you go through this method. That means that, even if you fixed the first problem, you would still be throwing away the old array without releasing it each time you passed through the method, so you'd still be leaking these arrays. There are a bunch of ways you could fix this, but the easiest one is probably to simply test if you already have an array and only create it if you haven't:
if(!images) {
images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
}
(Since all instances of this class have an identical list of image names, you could instead store the array in a static variable so it'd be shared between them—perhaps initialized by calling dispatch_once—but this isn't likely to make a difference unless you have many instances of this view controller on screen at the same time.)
Suppose in my Database Manager which is singleton.
+ (SWDatabaseManager *)retrieveManager
{
#synchronized(self)
{
if (!sharedSingleton)
{
sharedSingleton = [[SWDatabaseManager alloc] init];
}
return sharedSingleton;
}
}
- (NSArray *)getProductDetails:(NSString *)someString
{
NSArray *temp = [self getRowsForQuery:someString];
return temp;
}
- (NSArray *)getRowsForQuery:(NSString *)sql
{
sqlite3_stmt *statement=nil;
NSMutableArray *arrayResults = [NSMutableArray arrayWithCapacity:1];
//
//Fetching data from database and adds them in to arrayResults
//
return arrayResults;
}
Now from some view controller i am calling function of Database manager like this....
[self getProductServiceDidGetDetail:[[SWDatabaseManager retrieveManager] getProductDetail: #"SomeQuery"]
- (void)getProductServiceDidGetDetail:(NSArray *)detailArray
{
[self setDataSource:[NSArray arrayWithArray:detailArray]];
[self.tableView reloadData];
}
Questions are ...
When arrayResult of getRowsForQuery will release?
Do i need to assign nil to detailArray of getProductServiceDidGetDetail?
Is there any memory leaks?
Suggestion will be appreciated.
ARC does automatic memory management. So it releases everything (your array), when you are done using it.
ARC works by adding code at compile time to ensure that objects live
as long as necessary, but no longer. Conceptually, it follows the same
memory management conventions as manual reference counting (described
in Advanced Memory Management Programming Guide) by adding the
appropriate memory management calls for you.
To help you understand better you might want to read apples docs on ARC.
You will not need to assign nil to the array, nor do you have to worry about memory leaks.
You do not have to (indeed cannot) release instance variables, but you
may need to invoke [self setDelegate:nil] on system classes and other
code that isn’t compiled using ARC.
Quick question: I use lots of NSObject derived classes and am wondering how to properly cleanup class properties that may own instances of other classes (in the snippet below this is an array of custom class instances). Is my usage of new and finalize below correct?
My understanding is that new is a convenience method that calls both alloc and init, and finalize gets called before dealloc - at least this is what I have gleaned from reading the docs. Do I have this right?
Thanks for any tips/best practices, etc!
- (id)new {
waffleArray = [[NSMutableArray alloc] initWithCapacity:kCellCount];
for (int i = 0; i < kCellCount; i++) {
WaffleCell * cell = [WaffleCell new];
[waffleArray addObject:cell];
}
return self;
}
// clean up
- (void)finalize {
[waffleArray removeAllObjects];
waffleArray = nil;
[super finalize];
}
new on NSObject is a class method, not an instance method as you have it. Also I don't really see why you'd overload new. It would be more common to overload init so something like this:
- (id)init {
if ((self = [super init])) {
waffleArray = [[NSMutableArray alloc] initWithCapacity:kCellCount];
for (int i = 0; i < kCellCount; i++) {
WaffleCell * cell = [WaffleCell new];
[waffleArray addObject:cell];
}
}
return self;
}
As for finalize, you really don't need to do that. This is what Apple says about it:
The garbage collector invokes this method on the receiver before disposing of the memory it uses. When garbage collection is enabled, this method is invoked instead of dealloc.
With ARC enabled you wouldn't need to do anything and since the garbage collector will not be running anyway, finalize won't get called anyway. ARC will automatically generate code which will release waffleArray in dealloc for you, which is enough for proper memory management in this case because waffleArray's retain count will then drop to 0, be deallocated itself which will go and release the objects in the array.