iphone - 关联对象何时被释放?

标签 iphone objective-c cocoa-touch

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

问题是对象 B 似乎在 对象 A 之后被释放,这意味着它已经太晚了,无法将自己作为对象 A 的 KVO 观察者移除。我知道这一点是因为我收到 NSKVODeallocateBreak 异常,随后是对象 B 的 dealloc 中的 EXEC_BAD_ACCESS 崩溃。

有谁知道为什么对象 B 在对象 A 之后使用 OBJC_ASSOCIATION_RETAIN 被释放?关联对象是否在 释放后释放?他们会自动发布吗?有谁知道改变这种行为的方法吗?

我正在尝试通过类别向类中添加一些内容,因此我无法覆盖任何现有方法(包括 dealloc),而且我也不想特别麻烦 swizzling。在对象 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 一时兴起而中断。是的,我在 Apple 从事 UIKit 框架方面的工作。

这意味着您将不得不找到另一种方法来执行此操作,可能是通过稍微更改您的 View 布局。

关于iphone - 关联对象何时被释放?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6039309/

相关文章:

ios - 按钮上的标签,如何在 iOS7 中突出显示按下按钮时的文本?

ios - 将 TesseractOCR Objective-C 框架与 Swift iOS 应用程序一起使用(桥接头问题)

iphone - 删除 UITableViewCell 和该单元格的所有数据

iOS - UITextView 在 Storyboard中关闭键盘

iphone - 当键盘未进行更改时检测 UITextField 内容的更改

c++ - Exiftool 获取图像信息

ios - UIImages NSURL 和线程

iphone - NSRangeException',原因 : '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array' when presenting view controller

iphone - Xcode 5 中的代码签名

cocoa-touch - 来自 UIImage 的数据 URL/PNG