什么时候释放关联对象? [英] When does an associated object get released?

查看:261
本文介绍了什么时候释放关联对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



问题是对象B似乎在对象A之后解除分配 ,这意味着它太晚了,不能将其自身删除为对象A的KVO观察者。我知道这是因为我得到NSKVODeallocateBreak异常,然后EXEC_BAD_ACCESS在对象B的dealloc中崩溃。 p>

有人知道为什么对象B在具有OBJC_ASSOCIATION_RETAIN的对象A之后被释放?在解除分配后,相关物件是否会获得释出? 他们得到自动释放吗?有没有人知道改变这种行为的方法?



我试图通过类别添加一些东西,所以我不能覆盖任何现有的方法包括dealloc),我不是特别想混乱swizzling。我需要一些方法在对象A被释放之前解除关联和释放对象B.



EDIT - 这里是我试图得到工作。如果相关对象在UIImageView完全释放之前被释放,这一切都可以工作。我看到的唯一解决方案是在我自己的dealloc方法swizzle,并swizzle回原来,为了调用它。这很麻烦。



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){
UIActivityIndi​​catorView * spinnerView =(UIActivityIndi​​catorView *)[self viewWithTag:UIIMAGEVIEW_SPINNER_TAG];
if(!spinnerView){
spinnerView = [[[UIActivityIndi​​catorView alloc] initWithActivityIndi​​catorStyle:UIActivityIndi​​catorViewStyleWhiteLarge] 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:@imagedelegate:self callback:@selector(zsImageChanged)] autorelease];
frameWatcher = [[[ZSPropertyWatcher alloc] initWithObject:self keyPath:@framedelegate:self callback:@selector(zsFrameChanged)] autorelease];

[self zsShowSpinner:!self.image];
} else {
//删除微调框
[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 * obserObject;
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 observeObject,keyPath;

- (id)initWithObject:(NSObject *)anObject keyPath:(NSString *)aKeyPath delegate:(id)aDelegate callback:(SEL)aSelector {
if(!anObject ||! aKeyPath){
//前提条件
self = nil;
return self;
}

self = [super init];
if(self){
observeObject = anObject;
keyPath = aKeyPath;
delegate = aDelegate;
delegateCallback = aSelector;

[obserObject addObserver:self forKeyPath:keyPath options:0 context:nil];
}
return self;
}

- (void)dealloc {
[obserObject 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 方法已完成后,发布关联对象。


I'm attaching object B via associative reference to object A. Object B observes some properties of object A through KVO.

The problem is that object B seems to be deallocated after object A, meaning its too late to remove itself as a KVO observer of object A. I know this because I'm getting NSKVODeallocateBreak exceptions, followed by EXEC_BAD_ACCESS crashes in object B's dealloc.

Does anyone know why object B is deallocated after object A with OBJC_ASSOCIATION_RETAIN? Do associated objects get released after deallocation? Do they get autoreleased? Does anyone know of a way to alter this behavior?

I'm trying to add some things to a class through categories, so I can't override any existing methods (including dealloc), and I don't particularly want to mess with swizzling. I need some way to de-associate and release object B before object A gets deallocated.

EDIT - Here is the code I'm trying to get working. If the associated objects were released prior to UIImageView being completely deallocated, this would all work. The only solution I'm seeing is to swizzle in my own dealloc method, and swizzle back the original in order to call up to it. That gets really messy though.

The point of the ZSPropertyWatcher class is that KVO requires a standard callback method, and I don't want to replace UIImageView's, in case it uses one itself.

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

解决方案

The accepted answer to this related question explains the deallocation timeline of objects. The upshot is: Associated objects are released after the dealloc method of the original object has finished.

这篇关于什么时候释放关联对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆