[英]iPhone: calling release on an object: Does it get released right away?
[英]When does an associated object get released?
我通过关联引用将对象B附加到对象A.对象B观察对象A到KVO的一些属性。
问题是对象B似乎在对象A 之后被释放,这意味着它太迟了以将其自身移除为对象A的KVO观察者。我知道这是因为我得到NSKVODeallocateBreak异常,然后EXEC_BAD_ACCESS在对象B的dealloc中崩溃。
有没有人知道为什么对象B在具有OBJC_ASSOCIATION_RETAIN的对象A之后被释放? 解除分配后是否释放相关对象? 他们是否被自动释放? 有谁知道改变这种行为的方法?
我试图通过类别向类添加一些东西,所以我不能覆盖任何现有的方法(包括dealloc),而且我并不特别想要混乱。 在对象A被释放之前,我需要一些方法来解除关联并释放对象B.
编辑 - 这是我正在努力工作的代码。 如果在完全取消分配UIImageView之前释放了关联的对象,那么这一切都可以正常工作。 我所看到的唯一解决方案是在我自己的dealloc方法中调整,并调回原来以调用它。 但这真的很混乱。
ZSPropertyWatcher类的要点是KVO需要一个标准的回调方法,我不想替换UIImageView,以防它自己使用它。
的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
问题还要大:
UIKit不符合KVO标准
没有努力使UIKit类的键值可观察。 如果它们中的任何一个,它完全是巧合,并且在Apple的心血来潮中受到打破。 是的,我在UIKit框架上为Apple工作。
这意味着您将不得不寻找另一种方法来实现此目的,可能是稍微改变您的视图布局。
此相关问题的已接受答案解释了对象的释放时间线。 结果是:在原始对象的dealloc
方法完成后释放关联对象。
我认为你的情况是这样的:
1)对象A在其保留计数变为0后接收-dealloc
调用;
2)关联机制确保在某个时刻释放对象B(与解除分配不同)。
也就是说,我们并不确切知道在哪一点,但似乎我认为这种语义差异是对象A在对象A之后被解除分配的原因; 对象-dealloc
选择器无法-dealloc
关联,因此当调用它的最后一个版本时,执行-dealloc
,并且只有在此之后关联机制才能向对象B发送-release
...
还看看这篇文章 。
它还指出:
现在,当取消分配objectToBeDeallocated时,objectWeWantToBeReleasedWhenThatHappens将自动发送一个-release消息。
我希望这有助于解释您的体验。 至于其余的,我帮不了多少......
编辑:在DougW的评论之后继续进行如此有趣的猜测......
如果关联机制在释放对象A时被“破坏”(继续你的例子),我会看到存在一种循环依赖的风险。
如果从释放方法(而不是dealloc)执行与关联相关的代码,则对于每个版本,您将检查“拥有”对象(对象A)是否具有保留计数1; 实际上,在这种情况下,你知道减少它的保留计数会触发dealloc,所以在这之前,你首先要释放相关的对象(你的例子中的对象B);
但是如果对象B又“拥有”了第三个对象,比如C,会发生什么? 会发生的情况是,在对象B上调用释放时,当对象B保留计数为1时,C将被释放;
现在,考虑对象C“拥有”该序列的第一个对象A的情况。如果,当接收上述版本时,C的保留计数为1,它将首先尝试释放其关联对象,是A;
另一方面,如果您从-dealloc发送释放,则这种循环依赖似乎不可能。
这是非常做作的,我不确定我的推理是否正确,所以随时评论它...
objc_getAssociatedObject()
用于OBJC_ASSOCIATION_RETAIN
关联返回一个自动释放的对象。 你可以在同一个runloop循环/自动释放池范围内调用它,因为对象A被释放了吗? (您可以通过将关联更改为NONATOMIC
来快速测试)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.