Strange ARC issue not releasing ivar in UIView sub

2019-03-29 21:26发布

问题:

Possible Duplicate:
Why is object not dealloc'ed when using ARC + NSZombieEnabled

I've got a very strange issue I'm seeing at the moment in a project. Put simply I have ViewA which owns ViewB (strong property). ViewA creates its ViewB in its initialiser. Both objects are subclasses of UIView.

I have overridden dealloc in both and put a log line and a break point to see if they get hit. It seems that ViewA's dealloc is being hit but not ViewB's. However if I put in a self.viewB = nil in the dealloc of ViewA then it is hit.

So basically it's something like this:

@interface ViewA : UIView
@property (nonatomic, strong) ViewB *viewB;
@end

@implementation ViewA

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.viewB = [[ViewB alloc] initWithFrame:self.bounds];
        [self addSubview:self.viewB];
    }
    return self;
}

- (void)dealloc {
    //self.viewB = nil; ///< Toggling this commented/uncommented changes if ViewB's dealloc gets called.
    NSLog(@"ViewA dealloc");
}

@end

What I can't understand is why nil-ing viewB out makes a difference. If something else is holding onto viewB then it should make absolutely no difference if I nil it out or not here. And it shouldn't make a difference to the number of releases that ARC adds in either.

I can't seem to reproduce it in a minimal test case as yet, but I'm working on it. And I can't post the actual code I'm seeing this in unfortunately. I don't see that being an issue though because it's more the point that nil-ing it out shouldn't make a difference that I am confused by.

Can anyone see anything I am overlooking or give advice about where to look for debugging this problem?

Update:

I've found the problem. It appears that it's only a problem when NSZombieEnabled is set to YES. Well that is entirely mad and has to be a bug surely. Zombies should not affect how this works as far as I know. The objects should still go through the dealloc method. And what's more, it's just mad that it works if I nil out viewB in ViewA's dealloc.

回答1:

I've found that this appears to be a bug in the iOS implementation of zombies. Consider the following code:

#import <Foundation/Foundation.h>

@interface ClassB : NSObject
@end

@implementation ClassB
- (id)init {
    if ((self = [super init])) {
    }
    return self;
}
- (void)dealloc {
    NSLog(@"ClassB dealloc");
}
@end

@interface ClassA : NSObject
@property (nonatomic, strong) ClassB *b;
@end

@implementation ClassA
@synthesize b;
- (id)init {
    if ((self = [super init])) {
        b = [[ClassB alloc] init];
    }
    return self;
}
- (void)dealloc {
    NSLog(@"ClassA dealloc");
}
@end

int main() {
    ClassA *a = [[ClassA alloc] init];
    return 0;
}

That should output:

ClassA dealloc
ClassB dealloc

But with NSZombieEnabled set to YES, it outputs:

ClassA dealloc

As far as I can tell, this is a bug. It seems to only happen with iOS (both simulator and device) and does not happen when built and run for Mac OS X. I've filed a radar with Apple.

Edit: It turns out this has already been answered here - Why is object not dealloc'ed when using ARC + NSZombieEnabled . Managed to find it after I found out what the real problem was. It's nothing to do with ARC by the way.