some_id
some_id

Reputation: 29896

loadNibNamed leak of topLevelObjects

I am using custom cells and I call loadNibNamed:. This seems to cause a memory leak and I am not sure how to solve it. If I set the top level objects to nil afterwards, I still get the leak.

topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"customCell" owner:self options:nil];

I then do this

for (id currentObject in topLevelObjects){
        if ([currentObject isKindOfClass:[UITableViewCell class]]){
            cell =  (CustomCell *) currentObject;
            break;
        }
    }

and then mutate the properties on the cell.

The custom cell has a strong reference to a property, it is not a circular reference so I am not sure if this is the issue. What is the correct way to stop this abandoned memory when using ARC?

Upvotes: 0

Views: 2362

Answers (3)

Tim
Tim

Reputation: 1428

Matt's answer is spot on. Here's what the code looks like. Fixed my memory issue straight away as the cell is now loaded and dequeued as it should be.

First, register the nib

[self.tableView registerNib:[UINib nibWithNibName:@"customCell"
                                           bundle:nil] 
                           forCellReuseIdentifier:@"customCellID"];

Secondly...

CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"customCellID"];

The nib only needs a single tableviewcell in it and you're good to go!

Upvotes: 1

jlehr
jlehr

Reputation: 15597

Consider using UINib instead. UINib caches the contents of the nib file in memory, saving trips to the file system. So for example, you could create an instance of nib and reference in a property:

self.cellNib = [UINib nibWithNibName:@"customCell" bundle:nil];

In the nib file, bind the cell to a property of your table view controller. Then whenever you need an instance of the cell, ask the UINib instance to instantiate it for you, and a reference to the new cell will automatically be stored in the property.

[self.cellNib instantiateWithOwner:self options:nil];
cell = self.customCell;
self.customCell = nil;

This approach should help avoid the memory management issues you experienced working with the top-level objects array, and also significantly improve performance.

Upvotes: 0

matt
matt

Reputation: 535353

I suspect that your leak may be coming from the outlets in the nib. Note this phrase in the docs on loadNibNamed::

To establish outlet connections, this method uses the setValue:forKey: method, which may cause the object in the outlet to be retained automatically.

In other words, loadNibNamed sometimes imposes an extra retain because of the odd way key-value-coding works.

However, that is speculation, and there's no need for it, because there's no need for you to call loadNibNamed: in the first place!

You're using a custom UITableViewCell subclass, designed in a nib? Then why not do this the normal way? Make a nib containing one top-level object: the cell. Design the cell in the nib, set its class, hook up its outlets etc. In your code, call registerNib:forCellReuseIdentifier: on the table view, to tell the table view about your nib. When you later call dequeueReusableCellWithIdentifier:, if there are no free cells in the reuse pile, the table view will load your nib and hand you cell. No muss, no fuss.

Upvotes: 1

Related Questions