tags:

views:

37

answers:

3

I created a subclass of UITableView and wanted to use it with a UITableViewController to get the benefits of auto-scrolling when the keyboard appears. In the loadView for my view controller (derived from UITableViewController) I did the following:

- (void)loadView
{
    [super loadView];
    self.tableView = [[MyCustomTableView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame style:UITableViewStyleGrouped];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
}

Shouldn't this cause a leak with whatever self.tableView was referencing before the reassignment? I ran Build And Analyse and it didn't report it as a leak.

However, if I try to "be good"...

- (void)loadView
{
    [super loadView];
    [self.tableView release];
    // reassign code...
}

...all sorts of nasty crashes happen when my view is displayed. Can anyone explain to me whether simple reassignment causes a leak and, if so, how to do this properly?

Thanks in advance.

A: 

No, it won't cause a leak because the setter method setTableView: (which is called when you assign a new value to the property) will automatically release the old value. This is what properties are for.

Ole Begemann
A little late, but I'm confused. There is double-retaining and thus there will be a leak in the above example, no?
Kalle
You are correct, Kalle, there is a leak in the OP's code. I did not actually look at the assignment and only replied to the OP's question, "Shouldn't this cause a leak with whatever self.tableView was referencing before the reassignment?". Good catch.
Ole Begemann
A: 

As Ole Begemann said, When you assign using dot notation, there's more happening than just a plain assignment. Since setTableView: is a "retain property", it looks something like this:

- (void)setTableView:(UITableView *)newTable
{
  if(newTable != tableView) {
    [tableView release];
    tableView = [newTable retain];
  }
}

So the problem in your "be good" method is the following. If you just call release on the tableview object, you are not using the property setter, which means, you are not setting the tableView to nil. When you call self.tableView = [[MyCustomTableView alloc] init... the current tableView is pointing to a non-nil, already deallocated block of memory. This block of memory is the one that setTableView: will try to release again, raising a EXC_BAD_INSTRUCTION.

When you call release on some object the pointer of which may be reused in the future, always set it to nil afterward. This way you mark the pointer as deallocated and you won't risk to over-relese. Or just use a retain property, that will do the release for you when you assign it to nil.

duhanebel
A: 

Actually your example will cause a leak.

self.tableView = [[MyCustomTableView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame style:UITableViewStyleGrouped];

Setting self.tableView will increase retain by 1, and the alloc will increase retain by 1.

When you later on set self.tableView to another value, retain count is decreased by 1, leaving 1, thus your allocated MyCustomTableView will never be released.

The reason your loadView crashes is because the initial table view that is there before you alloc and add your own, is properly set up, with 1 retain. So when you release it, the system will try to release it too but by then it's already deallocated, and nastiness ensues.

The proper way to do it is to (auto)release the allocated MyCustomTableView:

self.tableView = [[[MyCustomTableView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame style:UITableViewStyleGrouped] autorelease];

That way, it will have 1 retain count only, which will drop to zero when you (re)set self.tableView.

Kalle