什么时候关联的对象被释放?

我通过关联引用将对象B附加到对象A.对象B通过KVO观察对象A的一些属性。

问题是对象B似乎对象A 之后被释放,意味着它为时已晚,不能将自己作为对象A的KVO观察者移除。我知道这是因为我得到NSKVODeallocateBreakexception,然后EXEC_BAD_ACCESS在对象B的dealloc中崩溃。

有谁知道为什么对象B在OBJC_ASSOCIATION_RETAIN的对象A之后被释放? 关联对象是否在释放后被释放? 他们得到autoreleased? 有谁知道一种方法来改变这种行为?

我试图通过类添加一些东西到一个类,所以我不能覆盖任何现有的方法(包括dealloc),我不想特意搅乱。 在对象A被取消分配之前,我需要一些方法去关联和释放对象B.

编辑 – 这是我正在努力工作的代码。 如果关联的对象在UIImageView完全释放之前被释放,这将全部工作。 我看到的唯一的解决scheme是在我自己的dealloc方法中debugging,然后调用原来的方法来调用它。 那变得非常混乱。

ZSPropertyWatcher类的要点是KVO需要一个标准的callback方法,而且我不想replaceUIImageView,万一它自己使用。

的UIImageView + Loading.h

@interface UIImageView (ZSShowLoading) @property (nonatomic) BOOL showLoadingSpinner; @end 

的UIImageView + Loading.m

 @implementation UIImageView (ZSShowLoading) #define UIIMAGEVIEW_SPINNER_TAG 862353453 static char imageWatcherKey; static char frameWatcherKey; - (void)zsShowSpinner:(BOOL)show { if (show) { UIActivityIndicatorView *spinnerView = (UIActivityIndicatorView *)[self viewWithTag:UIIMAGEVIEW_SPINNER_TAG]; if (!spinnerView) { spinnerView = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease]; spinnerView.tag = UIIMAGEVIEW_SPINNER_TAG; [self addSubview:spinnerView]; [spinnerView startAnimating]; } [spinnerView setEvenCenter:self.boundsCenter]; } else { [[self viewWithTag:UIIMAGEVIEW_SPINNER_TAG] removeFromSuperview]; } } - (void)zsFrameChanged { [self zsShowSpinner:!self.image]; } - (void)zsImageChanged { [self zsShowSpinner:!self.image]; } - (BOOL)showLoadingSpinner { ZSPropertyWatcher *imageWatcher = (ZSPropertyWatcher *)objc_getAssociatedObject(self, &imageWatcherKey); return imageWatcher != nil; } - (void)setShowLoadingSpinner:(BOOL)aBool { ZSPropertyWatcher *imageWatcher = nil; ZSPropertyWatcher *frameWatcher = nil; if (aBool) { imageWatcher = [[[ZSPropertyWatcher alloc] initWithObject:self keyPath:@"image" delegate:self callback:@selector(zsImageChanged)] autorelease]; frameWatcher = [[[ZSPropertyWatcher alloc] initWithObject:self keyPath:@"frame" delegate:self callback:@selector(zsFrameChanged)] autorelease]; [self zsShowSpinner:!self.image]; } else { // Remove the spinner [self zsShowSpinner:NO]; } objc_setAssociatedObject( self, &imageWatcherKey, imageWatcher, OBJC_ASSOCIATION_RETAIN ); objc_setAssociatedObject( self, &frameWatcherKey, frameWatcher, OBJC_ASSOCIATION_RETAIN ); } @end 

ZSPropertyWatcher.h

 @interface ZSPropertyWatcher : NSObject { id delegate; SEL delegateCallback; NSObject *observedObject; NSString *keyPath; } @property (nonatomic, assign) id delegate; @property (nonatomic, assign) SEL delegateCallback; - (id)initWithObject:(NSObject *)anObject keyPath:(NSString *)aKeyPath delegate:(id)aDelegate callback:(SEL)aSelector; @end 

ZSPropertyWatcher.m

 @interface ZSPropertyWatcher () @property (nonatomic, assign) NSObject *observedObject; @property (nonatomic, copy) NSString *keyPath; @end @implementation ZSPropertyWatcher @synthesize delegate, delegateCallback; @synthesize observedObject, keyPath; - (id)initWithObject:(NSObject *)anObject keyPath:(NSString *)aKeyPath delegate:(id)aDelegate callback:(SEL)aSelector { if (!anObject || !aKeyPath) { // pre-conditions self = nil; return self; } self = [super init]; if (self) { observedObject = anObject; keyPath = aKeyPath; delegate = aDelegate; delegateCallback = aSelector; [observedObject addObserver:self forKeyPath:keyPath options:0 context:nil]; } return self; } - (void)dealloc { [observedObject removeObserver:self forKeyPath:keyPath]; [keyPath release]; [super dealloc]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { [self.delegate performSelector:self.delegateCallback]; } @end 

这个相关问题的接受答案解释了对象的释放时间线。 结果是:关联对象在原始对象的dealloc方法完成后被释放。

甚至比你的-dealloc问题更大的是:

UIKit不符合KVO标准

没有努力使UIKit类的键值可观察。 如果他们中的任何一个是完全巧合的,并且会在苹果公司的突发事件中被打破。 是的,我在苹果的UIKit框架上工作。

这意味着你将不得不寻找另一种方式来做到这一点,可能通过稍微改变你的视图布局。

我觉得你的情况是这样的:

1)对象A在其保留计数变为0之后接收-dealloc调用;

2)关联机制确保对象B在某个时刻被释放(这与解除分配不同)。

也就是说,我们并不确切地知道在哪个点上,但是这种语义上的差异似乎是对象A在对象A之后被释放的原因; 对象A- -deallocselect器不能意识到关联,所以当它的最后一个版本被调用时, -dealloc被执行,并且只有在关联机制之后,才能发送一个-release到对象B …

也看看这个post 。

它还指出:

现在,当objectToBeDeallocated被释放时,objectWeWantToBeReleasedWhenThatHappens将被自动发送一个 – 释放消息。

我希望这有助于解释你正在经历的事情。 至于其余的,我不能有太大的帮助。

编辑:只是为了继续这样一个有趣的猜测之后DougW评论…

如果关联机制在释放对象A时发生“破坏”(继续执行您的示例),我认为存在循环依赖的风险。

  1. 如果与关联相关的代码是从release方法(而不是dealloc)执行的,那么对于每个版本,您将检查“拥有”对象(对象A)是否具有保留计数1; 事实上,在这种情况下,您知道减less其保留计数会触发dealloc,因此在执行此操作之前,首先会释放关联的对象(在您的示例中为对象B)。

  2. 但是在对象B的情况下会发生什么事情也轮stream“拥有”第三个对象,比如C? 会发生什么情况是在对象B调用释放的时候,当对象B保持计数为1时,C将被释放;

  3. 现在,考虑对象C“拥有”这个序列的第一个对象A的情况。如果当接收到上面的释放时,C具有保留计数1,则它将首先尝试并释放其关联的对象,其中是A;

    1. 但是A的释放计数仍然是1,所以另一个释放会发送给B,仍然有一个保留计数为1; 等等,在一个循环中。

另一方面,如果你从-dealloc发送释放这样的循环依赖似乎是不可能的。

这是非常人为的,我不知道我的推理是正确的,所以随时评论它…

objc_getAssociatedObject()关联的objc_getAssociatedObject()返回一个自动释放对象。 在对象A被释放的时候,你可能会在同一个runloop循环/自动释放池作用域中调用它吗? (您可以通过将关联更改为NONATOMIC来快速testing)。