Table view images never being released

I'm working on a major update to one of my applications and trying to cut down memory usage and making it cleaner and faster. I was using Instruments to profile the app and I was looking at the UIImage allocations, I have about 10 when the app starts out (although one is a status bar icon? Dont know why thats included). When i open up my Settings view controller (which is in a split view controller on iPad) it has basically an image with every table view cell, which is a lot. Presenting it for the first time adds 42 images. when I dismiss this view controller, there is still 52 images, when there should be only 10 now. If I present the controller again, there are now 91 images. This keeps going up and up. Instruments doesn't say there is a leak and I can't figure out what is happening. Each cell sets an image like

cell.imageView.image = [UIImage imageNamed:@"Help-Icon"];

How can I figure out why these images are not being released?

enter image description here

EDIT: I think Its deallocating the images now. I changed from setting the imageView image directly to setImage:, so a table cell looks like this now:

    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }
    cell.textLabel.text = NSLocalizedString(@"Homepage", @"Homepage");
                UIImage *cellImage = [UIImage imageNamed:@"Homepage-Icon"];
                [cell.imageView setImage:cellImage];
[MLThemeManager customizeTableViewCell:cell];
return cell;

MLThemeManager is a singleton that using a theme class to set properties of the cell to a theme, like the text label color, highlighted color, detail text label color, and background color.


Solution 1:

The possible reason here is because of your Settings view controller was not deallocated (not released from memory). No matter how many images are in that view controller when view controller release from memory it will deallocates (remove) all image object from memory.

How to check your view controller is de allocated (release) or not?

Steps:

  1. From Xcode long press on Run button. You get a small popup, Select Profile from that. And you can see new icon replaces the Run icon. (Similarly you can change it Run button to run the application).

enter image description here

  1. Click on this button will start to profile your application.

  2. Select Allocations section in Instruments window. There is a textfield above the allocation listing view. Click on that field and write your view controller name. (In your case SettingViewController).

enter image description here

  1. You can see only filter result related to that key word you just type. Now go to Simulator -> Simulate your flow. Pop back from your view controller and check in Instruments that after leaving that view controller if it's still in the list. If it is there in a list than your view controller is not release from memory and each time you open that view controller will increase memory and never release until application is running.

How we can de allocate view controller?

I'll try to explain some way which I know to de allocated controller.

[ 1 ] Override dealloc method in your view controller and release objects in that. Mainly mutable variable and delegates. Put a debug breakpoint on -dealloc method and make sure it is called when you left that controller.

Note: If you have create global variable for class like UIPickerView, UIPopoverController, UIImagePickerController etc. and set delegates for that than these delegates must be nil in -dealloc method.

e.g.

Objective C code:

//---------------------------------------------------------------

#pragma mark
#pragma mark Memory management methods

//---------------------------------------------------------------

- (void) dealloc {
    [self.tableView setDelegate:nil];
    [self.tableView setDataSource:nil];
}

Swift code:

deinit {
    self.tableView.dataSource = nil
    self.tableView.delegate = nil
}

[ 2 ] Global object of subclass also needs to override -dealloc method and release relevant objects and delegates. Similar their dealloc method must have to be called.

e.g.

Check this scenario: Let's say you have created a subclass of UIView (or any other view) with the name MenuView. And you have created a global variable of this class in your view controller, than it's dealloc method will be called before view controller's dealloc method.

@property (nonatomic, strong) MenuView    *menuView;

So here MenuView class needs to be override the -dealloc method and must be called before your view controller's dealloc method is called.

So that before leaving whole container its child views are release and removed from memory.

Check this in Instruments for MenuView class also that object is still their or not?

If it's dealloc method is not called than object of MenuView class is still in memory and your view controller class is referencing that object. So even if your view controller's -dealloc is called it will not deallocated because MenuView class object is live.


[ 3 ] You need to set weak reference to the IBOutlet. e.g.

Objective C code:

@property (nonatomic, weak) IBOutlet UITableView   *tableView;

Swift Code:

@IBOutlet weak var tableView: UITableView!

Feel free to ask if anything is not clear.

Solution 2:

Dont use the image asset... Copy the images to you a folder into your project an load it by code with imageWithContentOfFile it works to me

Solution 3:

Try to use imageWithContentsOfFile to load image.

As rmaddy said,imageNamed will let your image stay in cache,this is the reason for your memory problem