我通过对象 A 的关联引用来附加对象 B。对象 B 通过 KVO 观察对象 A 的一些属性。
问题是对象 B 似乎已被释放after对象 A,这意味着将自身作为对象 A 的 KVO 观察者删除为时已晚。我知道这一点是因为我收到 NSKVODeallocateBreak 异常,然后在对象 B 的 dealloc 中 EXEC_BAD_ACCESS 崩溃。
有谁知道为什么对象 B 在对象 A 之后使用 OBJC_ASSOCIATION_RETAIN 被释放?关联对象是否被释放after解除分配?它们会自动释放吗?有谁知道改变这种行为的方法?
我试图通过类别向类添加一些内容,因此我无法覆盖任何现有方法(包括 dealloc),而且我也不想特别混乱。在对象 A 被释放之前,我需要某种方法来取消关联并释放对象 B。
EDIT- 这是我正在尝试运行的代码。如果关联对象在 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