弱属性不使用 ARC 归零

发布于 2025-01-01 13:23:05 字数 605 浏览 6 评论 0原文

对于持有弱引用的对象,我有以下简单代码:

// 接口

@interface GMWeakRefObj : NSObject
@property (weak) id object;
@end

// 实现

@implementation GMWeakRefObj 
@synthesize object;
@end

当我运行以下测试代码时,它在第二个断言上失败:

NSData* d = [[NSData alloc] init];
GMWeakRefObj* weakRef = [[GMWeakRefObj alloc] init];
weakRef.object = d;
NSAssert(weakRef.object != nil, @"Reference wasn't assigned");
d = nil;
NSAssert(weakRef.object == nil, @"Reference wasn't zeroed"); // <-- FAIL

Aren ARC弱引用不应该归零吗?如果是这样,我做错了什么?

I have the following simple code for an object that holds a weak reference:

// interface

@interface GMWeakRefObj : NSObject
@property (weak) id object;
@end

// implementation

@implementation GMWeakRefObj 
@synthesize object;
@end

When I run the following test code it fails on the second assert:

NSData* d = [[NSData alloc] init];
GMWeakRefObj* weakRef = [[GMWeakRefObj alloc] init];
weakRef.object = d;
NSAssert(weakRef.object != nil, @"Reference wasn't assigned");
d = nil;
NSAssert(weakRef.object == nil, @"Reference wasn't zeroed"); // <-- FAIL

Aren't ARC weak references supposed to be zeroing? And if so what am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

塔塔猫 2025-01-08 13:23:05

尝试使用一些自定义类来代替 dNSData,例如 MyData。在其中实现dealloc方法并在其中设置断点。您将看到,dealloc 由自动释放池在最后一个 NSAssert 之后调用。只有在那一周之后,引用才会变为nil

添加:
看来我必须扩展我的答案才能弄清楚为什么它会这样工作。
首先,让我们看一下您的示例(来自注释):

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

它按预期工作,weakRefdata = nil 之后变为 nil。下一个示例也可以工作:

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
NSLog(@"%@", data);
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

但上一个示例不起作用:

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
NSLog(@"%@", weakRef);
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

唯一的区别是我们使用弱引用来输出日志。为什么?

(答案的其余部分可能是错误的:))

想象您NSLog(或我们在data = nil之前调用的任何其他函数/选择器) )依赖于它的参数不为nil。例如,它有“if (arg == nil) return;”一开始。

在多线程环境中,弱引用可以if之后变成nil

所以正确编写的函数应该是这样的:

  // ...
  T* local_arg = arg;   // NOTE: it is strong!
  if (local_arg == nil)
    return;
  // work with local_arg here, not with arg
  // ...

但通常我们不想在任何地方都这样做——它会很难看。所以我们要确保争论不会在中间的某个地方消失。编译器通过自动释放弱引用来为我们做到这一点。

因此,应该清楚您的 GMWeakRefObj 测试用例为何不起作用 - weakRef 在调用 setObject setter 之前自动释放。

Try some custom class class instead of NSData for d, e.g. MyData. Implement dealloc method in it and set breakpoint in it. You will see, that dealloc is called by autorelease pool after the last NSAssert. Only after that week reference will become nil.

ADD:
Looks like I have to extend my answer to make it clear, why it works that way.
First, lets look at your example (from comments):

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

It works as expected, weakRef become nil after data = nil. The next example works too:

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
NSLog(@"%@", data);
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

But the last example doesn't work:

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
NSLog(@"%@", weakRef);
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

The only difference is that we use weak reference to output log. Why?

(the rest of the answer can be wrong :) )

Imaging that you NSLog (or any other function/selector we call before data = nil) rely on it's argument not to be nil. For example, it has "if (arg == nil) return;" at the very beginning.

In multithreaded environment weak reference can become nil after if.

So properly written function should look like:

  // ...
  T* local_arg = arg;   // NOTE: it is strong!
  if (local_arg == nil)
    return;
  // work with local_arg here, not with arg
  // ...

But usually we don't want to do it everywhere -- it will be ugly. So we want to be sure that arguments will not disappear somewhere in the middle. Compiler does it for us by autoreleasing weak reference.

So, it should be clear how, why your GMWeakRefObj test case doesn't work -- weakRef is autoreleased before calling setObject setter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文