Aleksandar • Vacić

iOS bits and pieces

Debugging [CALayer retain]: message sent to deallocated instance

While working recently on an iPhone app, I had a subclass of UITableViewCell with a specific indicator, all defined like this:

1
2
3
4
5
@interface EmailListCell : UITableViewCell {
  UIImageView *indicator;
}

@property (nonatomic, retain) UIImageView *indicator;

In the implementation part - since the property is retained, I automatically added this:

1
2
3
4
5
6
7
8
9
@implementation EmailListCell

@synthesize indicator;

- (void)dealloc {
  [indicator release];
  indicator = nil;
  [super dealloc];
}

This is what I usually do, mostly mechanically, to not forget to add proper releasing. And it came back to bite me this time.

It was because of the initializing code I wrote after the code above:

1
2
3
4
5
6
7
8
9
10
11
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {

  if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
      indicator = [[UIImageView alloc] initWithImage:widgetEmpty];
      [self addSubview:indicator];
      [indicator release];
  }

  return self;

}

When testing this table view, it would - in some cases only - crash the app, with one of these messages shown:

1
2
*** -[CALayer retain]: message sent to deallocated instance 0x5c73490`
modifying layer that is being finalized 0x713a170`

These are thrown when you attempt to access UIView of any sort that is in the process of being deallocated. Catching these things is very tricky, because the objects mentioned are already gone, so even if you setup breakpoint to objc_exception_throw, it won’t help you much.

So, do you see where the issue is? :)

I am releasing indicator view twice. First time when it is created and second time in dealloc. The latter is not needed, since all subviews of the cell’s view will automatically be released along with it. The code I wrote would be correct if the init line used the property:

1
self.indicator = [[UIImageView alloc] initWithImage:widgetEmpty];

By using self, I’m actually raising the retain count to 2, so two release calls would be fine. Thus, the proper initialization code would be:

1
2
3
4
UIImageView *ind = [[UIImageView alloc] initWithImage:widgetEmpty];
self.indicator = ind;
[self addSubview:ind];
[ind release];

And then the indicator release in the dealloc is mandatory (and also a proper way when dealing with retained properties).