ARC和__unsaf_未保留



我认为我对ARC和选择适当的生存期限定符(__strong__weak__unsafe_unretained__autoreleasing)的正确用例有很好的理解。然而,在我的测试中,我发现了一个对我来说没有意义的例子

据我所知,__weak__unsafe_unretained都不添加保留计数。因此,如果没有其他指向该对象的__strong指针,则会立即释放该对象(不可变字符串是该规则的例外)。这个过程中唯一的区别是__weak指针被设置为零,而__unsafe_unretained指针被单独保留。

如果我创建一个指向一个简单的自定义对象(由一个NSString属性组成)的__weak指针,我会在尝试访问属性时看到预期的(null)值:

Test * __weak myTest = [[Test alloc] init];
myTest.myVal = @"Hi!";
NSLog(@"Value: %@", myTest.myVal); // Prints Value: (null)

类似地,我预计__unsafe_unretained生存期限定符会由于产生的悬空指针而导致崩溃。然而,事实并非如此。在下一次测试中,我看到了实际值:

Test * __unsafe_unretained myTest = [[Test alloc] init];
myTest.myVal = @"Hi!";
NSLog(@"Value: %@", myTest.myVal); // Prints Value: Hi!

为什么__unsafe_unretained对象没有被解除分配?

[EDIT]:正在释放对象。。。如果我试图用NSLog(@"%@", myTest);替换第2-3行,应用程序就会崩溃(并且在第一行之后立即调用Test中被覆盖的dealloc)。我知道,即使使用__unsafe_unretained,不可变字符串也将继续可用,并且指向NSString的直接指针也可以工作。我只是感到惊讶的是,我可以在一个已解除分配的对象上设置一个属性(第2行),然后它可以从指向它所属的已解除分配对象的指针中解除引用(第3行)!如果有人能解释这一点,它肯定会回答我的问题。

我只是很惊讶,我可以在一个已解除分配的对象上设置一个属性(第2行),然后可以从指向它所属的已解除分配对象的指针中取消引用它(第3行)!如果有人能解释这一点,它肯定会回答我的问题。

当对象被解除分配时,它不是零。由于您有一个指向已解除分配对象的指针,并且属性值存储在该指针的某个偏移量处,因此在解除分配后,存储和检索该属性值可能会成功,也很可能由于某种原因,一切都会崩溃。

您的代码工作非常脆弱,请尝试使用"调试时显示反汇编"进行调试,然后逐步完成,您可能会遇到访问冲突,或者删除Xcode本身。。。

在C、Objective-C、C++或任何一个家族中发生奇怪的事情,你都不应该感到惊讶;相反,为发生的这么少奇怪的事情保留你的惊喜!

因为objc中的常量字符串是指向堆地址的常量指针,并且该地址仍然有效。

评论后编辑:

也许是因为测试对象地址处的内存没有被覆盖,并且仍然包含该对象?推测。。。。

您可以通过实现其-dealloc方法并添加一些简单的日志记录来查看何时释放Test

但是,即使Test立即解除分配,它在RAM中占用的内存也可能在调用myVal时保持不变。

@"hi!"生成一个静态全局常量字符串实例,该实例实际上是一个单例。因此,它永远不会被释放,因为它一开始并没有真正被分配(至少,它真的不是一个正常的堆分配)。

任何时候,如果您想探索对象的寿命问题,请始终使用NSObject的子类来保证行为,并通过重写行为来简化日志挂钩。

没有什么奇怪的…您需要至少有一个对对象的强引用才能使其保持活动状态。

Test * anTest = [[Test alloc] init];
Test * __weak myTest = anTest;
myTest.myVal = @"Hi!";
NSLog(@"Value: %@", myTest.myVal); // Prints Value: (Hi)

最新更新