[英]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.