关联对象什么时候被释放?

2023-12-11

我通过对象 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

甚至比你的还大-dealloc问题是这样的:

UIKit 不符合 KVO 标准

没有做出任何努力来使 UIKit 类的键值可观察。如果其中任何一个are, it is entirely纯属巧合,并且可能会因 Apple 的一时兴起而中断。是的,我在 Apple 从事 UIKit 框架的工作。

这意味着您将不得不找到另一种方法来做到这一点,可能是通过稍微改变您的视图布局。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关联对象什么时候被释放? 的相关文章

随机推荐

  • Rails 检测请求是否为 AJAX

    在我的操作中 我希望仅在从 AJAX 请求调用时才响应处理 我该如何检查 我想做这样的事情 def action model Model find params id respond to do format if wasAJAXReque
  • Vue Getter & Setter 而不是后端响应中的实际值

    我是 Vue 新手 我很难理解为什么我的问题会发生 当我调用后端来检索一些数据时 响应如下 id Getter Setter name Getter Setter season Getter Setter number Getter Set
  • 使用 OpsHub 工具从本地 TFS 迁移到在线 TFS 速度缓慢

    我们有一个 50GB 的本地 TFS 集合 我们希望将其迁移到在线 TFS 我们现在使用 OpsHub Visual Studio Online Migration Utility 进行测试 一些单独的测试项目的转换似乎工作正常 因此现在我
  • Boost 的 lexical_cast 从双精度到字符串精度

    我正在使用一个不幸使用的库boost lexical cast从 a 转换double to a string 我需要能够明确地反映我这边的行为 但我希望这样做而不传播boost 我可以使用以下方法保证相同的行为吗to string spr
  • 通过 OAuth 对 Google API 进行身份验证调用时遇到问题

    当我尝试使用服务器到服务器身份验证调用 Google Directory API 时 收到错误消息 未授权访问此资源 api 我做了什么 在 Google Developers Console 中创建了一个应用程序 下载私钥并查找服务帐户名
  • 如何使用外部 jar 文件为 java 制作 makefile

    我需要制作一个 makefile 用外部 jar 文件编译和执行我的类 我有4节课 sync java FileSynchroniser java DirectoryTracer java and SyncFileTracer java 我
  • 具有自动内存清理功能的图像下载器

    我有一个项目列表 简单的列表框 其中包含主从基础上的图像 如果用户单击列表项目 则会打开详细信息页面 我遇到了非常著名的图像内存泄漏问题 描述here here here and here 一种可能的方法是遍历所有图像当导航自并清洁它们 I
  • 是否可以在 WindowButtonMotionFcn 中使用多个回调?

    我创建了一个类 为构造中的图形添加功能 这个类创建一个监听器WindowMouseMotion事件 但是 为了触发此事件 我必须为该图添加一个虚拟回调函数WindowButtonMotionFcn财产 我首先检查该属性是否已填充 如果不是
  • Java 泛型和泛型类型

    我有一个 ExtA 类 其中包含一个过滤函数来过滤 ArrayList public ExtA
  • leaflet 插件和 leafletProxy 以 polylineDecorator 为例

    另请参考一个问题leaflet 插件和 leafletProxy 我想用折线装饰器插件在 R 的传单中 下列的有关如何使用 R 中的传单插件的说明 如果我将该方法与leaflet 请参阅第一个示例 它按我想要的方式工作 但如果我尝试使用相同
  • Excel 互操作:_Worksheet 还是 Worksheet?

    我目前正在撰写有关动态类型的文章 并给出了 Excel 互操作的示例 我以前几乎没有做过任何 Office 互操作 这表明了这一点 这MSDN Office 互操作教程对于 C 4 使用 Worksheet界面 但还有一个Worksheet
  • 在 Python Social Auth 中获取 Facebook 用户喜欢

    我正在使用 Django 启动一个项目 并尝试允许用户使用 Facebook 登录 出于网站目的 我想将用户喜欢存储在我的数据库中 我以 Python Social Auth 我用于该项目的库 上的示例应用程序为例 并且编写了一个用于在数据
  • 在 IIS 服务器上发布 C# 控制台应用程序

    是否可以在 IIS 服务器上部署控制台 C 应用程序 我有一个 exe 文件在一台机器上运行 该文件从命名管道获取数据 我的 C 应用程序从管道获取此数据并将其发送到使用 TCP 套接字运行的 Web 应用程序 我想知道是否可以部署我的 C
  • JavaScript 分离轴定理

    我正在尝试使用 JavaScript 中的分离轴定理来检测两个正方形碰撞 一个旋转 一个不旋转 尽管我很努力 但我无法弄清楚这在 JavaScript 中会是什么样子 也找不到任何 JavaScript 示例 请帮忙 用简单的数字或 Jav
  • 在 Google Colab 文本单元中插入图像

    我想将图像插入到 google colab 中的文本单元格中 该图像已上传到 google 云端硬盘中 为此 我使用 Markdown 语法 名称 获取可共享链接 但这不起作用 我的链接如下所示 https drive google com
  • 为什么 CSS :active on link 不会突出显示当前页面链接?

    我有下面的代码用于列表到母版页 div ul li a href default aspx Home a li li a href page1 aspx Page1 a li ul div with css header a hover c
  • 同时访问<地址>,但修改需要独占访问

    我想指定一个属性 它是一个用于在函数中进行操作的数组myFunc 但我收到此错误 这是我的代码草图 self data MyObject func myFunc x inout Int y inout Int manipulation co
  • 从Flask路线启动scrapy

    我想构建一个爬虫 它获取要抓取的网页的 URL 并将结果返回到网页 现在我从终端启动 scrapy 并将响应存储在文件中 当某些输入发布到 Flask 时 如何启动爬虫 处理并返回响应 您需要在 Flask 应用程序中创建一个 Crawle
  • C# 中如何获取目录中的文件列表

    所以 我正在用 C 开发 mp3 播放器 但我遇到了一个问题 我想从列表中的目录中获取所有文件 因此您可以选择 您要播放的歌曲 这是获取文件的代码 string music Directory GetFiles dir mp3 然后就可以玩
  • 关联对象什么时候被释放?

    我通过对象 A 的关联引用来附加对象 B 对象 B 通过 KVO 观察对象 A 的一些属性 问题是对象 B 似乎已被释放after对象 A 这意味着将自身作为对象 A 的 KVO 观察者删除为时已晚 我知道这一点是因为我收到 NSKVODe