Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/41.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Iphone 关联对象何时释放?_Iphone_Objective C_Cocoa Touch - Fatal编程技术网

Iphone 关联对象何时释放?

Iphone 关联对象何时释放?,iphone,objective-c,cocoa-touch,Iphone,Objective C,Cocoa Touch,我通过关联引用将对象B附加到对象A。对象B通过KVO观察对象A的一些属性 问题是,对象B似乎在对象A之后被解除分配,这意味着作为对象A的KVO观察器将其自身删除为时已晚。我知道这一点,因为我遇到了NSKVODeallocateBreak异常,然后在对象B的解除分配中出现EXEC_BAD_访问崩溃 有人知道为什么对象B在对象A之后被解除分配,而对象A具有OBJC_关联_RETAIN吗?解除分配后是否释放关联对象?他们会自动释放吗?有人知道改变这种行为的方法吗 我试图通过类别向类中添加一些内容,因此

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

问题是,对象B似乎在对象A之后被解除分配,这意味着作为对象A的KVO观察器将其自身删除为时已晚。我知道这一点,因为我遇到了NSKVODeallocateBreak异常,然后在对象B的解除分配中出现EXEC_BAD_访问崩溃

有人知道为什么对象B在对象A之后被解除分配,而对象A具有OBJC_关联_RETAIN吗?解除分配后是否释放关联对象?他们会自动释放吗?有人知道改变这种行为的方法吗

我试图通过类别向类中添加一些内容,因此我无法覆盖任何现有方法(包括dealloc),并且我不想特别麻烦swizzling。在对象A被释放之前,我需要一些方法来解除关联并释放对象B

编辑-以下是我正在尝试使用的代码。如果在UIImageView完全释放之前释放了关联对象,那么这一切都会起作用。我看到的唯一解决方案是在我自己的dealloc方法中快速切换,然后快速切换回原始方法以调用它。不过这真的很混乱

zPropertyWatcher类的要点是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
zPropertyWatcher.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
zPropertyWatcher.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

我认为你的情况是这样的:

1) 对象A在其保留计数变为0后接收到
-dealloc
调用

2) 关联机制确保对象B在某个时刻被释放(这与解除分配不同)

i、 例如,我们不知道具体在哪一点,但在我看来,这种语义差异很可能是对象B在对象A之后被释放的原因;对象A
-dealoc
选择器无法识别关联,因此当调用其上的最后一个版本时,
-dealoc
将被执行,并且只有在这之后关联机制才能向对象B发送一个
-release

再看一看

它还指出:

现在,当ObjectToBedAllocated被解除分配时,ObjectWewantToBereAllocated将自动发送一条-release消息

我希望这有助于解释你的经历。 至于其余的,我帮不了什么忙

编辑:在DougW的评论之后,继续这样一个有趣的猜测

如果在释放对象a时关联机制被“破坏”,我会看到存在某种循环依赖的风险(继续你的例子)

  • 如果关联相关代码是从release方法(而不是dealloc)执行的,那么对于每个release,您将检查“拥有”对象(对象A)的retain计数是否为1;事实上,在这种情况下,您知道减少其retain计数将触发dealloc,因此在这样做之前,您应该首先释放关联的对象(示例中的对象B)

  • 但如果对象B同时“拥有”第三个对象,比如说对象C,会发生什么呢?在对象B上调用release时,当对象B的retain count为1时,C将被释放

  • 现在,考虑对象C“拥有”这个序列中的第一个对象A对象,如果在接收到上面的发布时,C的保留计数为1,它将首先尝试并释放其相关的对象,这是一个;p>

  • 但是A的释放计数仍然是1,因此另一个释放将被发送到B,B的保留计数仍然是1;等等,在一个循环中
  • 另一方面,如果您从-dealoc发送发布,那么这种循环依赖性似乎是不可能的


    这是相当做作的,我不确定我的推理是否正确,所以请随意评论…

    比您的
    -dealloc
    问题更大的是:

    UIKit不兼容KVO

    没有努力使UIKit类的键值可见。如果它们中有任何一个是,那么这完全是巧合,可能会因苹果的突发奇想而被打破。是的,我在苹果的UIKit框架上工作


    这意味着您必须找到另一种方法来执行此操作,可能需要稍微更改视图布局。

    objc\u getAssociatedObject()
    对于
    objc\u ASSOCIATION\u RETAIN
    关联返回一个自动删除的对象。您可能会在对象A解除分配时在同一runloop cycle/autorelease池作用域中调用它?(您可能可以通过将关联更改为非原子来快速测试这一点)。

    的公认答案解释了对象的解除分配时间表。结果是:在原始对象的
    dealloc
    方法完成后,关联对象被释放。

    有趣的问题!你能提供更多关于为什么需要这种特殊设置的信息吗?除了“对象A”和“对象B”之外,再多描述一点,可能会大大有助于我们帮助您。:)@戴夫·德隆——我正要出去吃午饭,但我回来后会发布一些源代码。基本上,我试图通过一个类别扩展UIImageView,给它一个属性标志,如果图像属性为nil,它将显示一个微调器。我想使用一个类别,这样我就不需要子类,并且可以在任何地方使用它。我试图做到这一点而不重写任何基类方法(有些只是为了好玩;)。@davedelong-代码够你用吗?;)。我想做我想做的事情的唯一方法是,当用户将showLoadingSpinner标志设置为YES时,在我自己的dealloc方法中swizzle,在那里将我的关联对象设置为nil(释放它们),然后在