1

I'm currently working on fixing some iOS 7 display issues on a legacy app which was not built with ARC, and have run into some inconsistent behaviour with the dealloc method between iOS versions 6 & 7. I can't find any other mention of these changes in any documentation or community discussions, so I wonder if anyone here could shed some light on what's happening here?

My previous code, (which works in iOS6, looks like this):

@interface MyViewController()
@property (retain) AdHandler *adHandler;
@end

@implementation MyViewController

@synthesize adHandler = _adHandler;

- (id) initWithAdHandler:(AdHandler*)anAdHandler
{
    self = [super init];
    _adHandler = [anAdHandler retain];
    return self;
}

- (void)dealloc
{    
    [super dealloc];

    [_adHandler release];
    _adHandler = nil;
}

...

@end

When stepping through the code in iOS 6, I've found that after the dealloc statement, [_adHandler retainCount] is still positive, and the object is still available.

In iOS 7 however, after the dealloc statement, retainCount has somehow hit zero, and the _adHandler object has been dealloc'd, and therefore my call to release causes an EXC_BAD_ACCESS.

I can fix this simply by moving my [adHandler release] call to before the dealloc call, but my question is why is this happening? Why is dealloc releasing objects that it has no responsibility for? Is there any documentation anywhere on why dealloc behaviour has changed in this way?

majackson
  • 2,823
  • 6
  • 22
  • 38

2 Answers2

6

After [super dealloc] the instance is garbage and whatever happens is rather random and non-deterministic. As @bneely wrote, [super dealloc] must be last.

Best practice: convert to ARC.

As for retainCount, there are no guarantees what it's value may be, don't use it, it just caused confusion. In your case you destroyed the class instance by calling [super dealloc] and then expect the instance to behave as if it still exists. It can't, it has been destroyed and is now just some non-deterministic bits in memory.

Mike Abdullah
  • 14,933
  • 2
  • 50
  • 75
zaph
  • 111,848
  • 21
  • 189
  • 228
  • But after `dealloc`, `_adHandler` still contains a memory address, and the object in that memory address should not have had `release` called on it, so should still exist? – majackson Sep 26 '13 at 11:17
  • 1
    @majackson The memory can be already overwritten, especially in Debug mode – Sulthan Sep 26 '13 at 11:19
  • @Sulthan Why would the memory be overwritten if the retainCount has not hit zero? Potentially other objects could have references to that object! Or do you mean the memory that contains the address to the object? – majackson Sep 26 '13 at 11:22
  • @majackson First bad assumption: That the retainCount will ever be zero. Second bad assumption: That a deallocated object can somehow be in a working state. Memory is released and reused, that is how these systems work. – zaph Sep 26 '13 at 12:02
  • 1
    @majackson When `super dealloc` has been called, the memory is returned to the system. It can be ovewritten immediately. If you call `self.something` later, the pointer you get is completely random (undefined). `retainCount` value is completely irrelevant because you cannot understand it without actually looking into Apple's source code. – Sulthan Sep 26 '13 at 13:42
  • @Sulthan This makes sense to me: the object in question hasn't (necessarily) been destroyed, but the pointer that was previously referencing it has been overwritten. Essentially, the answer to my question is that iOS7 is significantly more aggressive at overwriting this data than iOS6. If you'd posted an answer instead of a comment, that would be my accepted answer! Thanks. – majackson Sep 26 '13 at 13:51
  • @Zaph I think you misunderstood my previous comment. In any case, Sulthan has made the matter clearer in my mind. Thanks for the suggestions. – majackson Sep 26 '13 at 13:52
  • 1
    @majackson I am pretty sure the code would trigger warnings already in Xcode 4. Note that in Debug mode the memory can be overwritten immediately for the purpose to trigger an exception if you access it again. Having code after `[super dealloc]` was always wrong and dangerous. – Sulthan Sep 26 '13 at 14:13
0

You should never use retain count.

The values it returns cannot be interpreted by you in any reasonable way. Thus it's entirely expected to see different results on different iOS versions, devices, with different code bitness, etc.


On a side note, do you consider switching to ARC? The code would greatly simplify. Also note that if you're implementing a subclass of UIViewController, you shouldn't initialize it with init. Rather just declare the property and ARC will take care of its setters and getters:

@interface MyViewController : UIViewController
@property AdHandler *adHandler;
@end

// somewhere else 
MyViewController * mvc = ... from nib or in some other way ...
mvc.adHandler = myAdHandler;

Now you're guaranteed no bad accesses.

Community
  • 1
  • 1
ilya n.
  • 18,398
  • 15
  • 71
  • 89