什么时候关联的对象被释放?
我通过关联引用将对象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- -dealloc
select器不能意识到关联,所以当它的最后一个版本被调用时, -dealloc
被执行,并且只有在关联机制之后,才能发送一个-release
到对象B …
也看看这个post 。
它还指出:
现在,当objectToBeDeallocated被释放时,objectWeWantToBeReleasedWhenThatHappens将被自动发送一个 – 释放消息。
我希望这有助于解释你正在经历的事情。 至于其余的,我不能有太大的帮助。
编辑:只是为了继续这样一个有趣的猜测之后DougW评论…
如果关联机制在释放对象A时发生“破坏”(继续执行您的示例),我认为存在循环依赖的风险。
-
如果与关联相关的代码是从release方法(而不是dealloc)执行的,那么对于每个版本,您将检查“拥有”对象(对象A)是否具有保留计数1; 事实上,在这种情况下,您知道减less其保留计数会触发dealloc,因此在执行此操作之前,首先会释放关联的对象(在您的示例中为对象B)。
-
但是在对象B的情况下会发生什么事情也轮stream“拥有”第三个对象,比如C? 会发生什么情况是在对象B调用释放的时候,当对象B保持计数为1时,C将被释放;
-
现在,考虑对象C“拥有”这个序列的第一个对象A的情况。如果当接收到上面的释放时,C具有保留计数1,则它将首先尝试并释放其关联的对象,其中是A;
- 但是A的释放计数仍然是1,所以另一个释放会发送给B,仍然有一个保留计数为1; 等等,在一个循环中。
另一方面,如果你从-dealloc发送释放这样的循环依赖似乎是不可能的。
这是非常人为的,我不知道我的推理是正确的,所以随时评论它…
objc_getAssociatedObject()
关联的objc_getAssociatedObject()
返回一个自动释放对象。 在对象A被释放的时候,你可能会在同一个runloop循环/自动释放池作用域中调用它吗? (您可以通过将关联更改为NONATOMIC
来快速testing)。