ARC和__unsafe_unretained

时间:2012-11-20 16:11:55

标签: objective-c automatic-ref-counting

我认为我非常了解ARC以及选择合适的终身限定符的正确用例(__strong__weak__unsafe_unretained__autoreleasing) 。但是,在我的测试中,我发现了一个对我没有意义的例子。

据我了解,__weak__unsafe_unretained都不会添加保留计数。因此,如果没有指向该对象的其他__strong指针,则会立即取消分配(使用不可变字符串作为此规则的例外)。这个过程的唯一区别是__weak指针设置为nil,__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对象不会被解除分配?

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

5 个答案:

答案 0 :(得分:2)

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

评论后编辑:

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

答案 1 :(得分:2)

  

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

取消分配对象时,它不会归零。由于你有一个指向解除分配对象的指针,并且属性值存储在该指针的某个偏移量处,因此可能存储和检索该属性值将在取消分配后成功,所以一切都很可能会因某种原因而爆炸。

您的代码工作非常脆弱,尝试使用“在调试时显示反汇编”进行调试并单步执行,您可能会遇到访问冲突,或者取消Xcode本身......

在C,Objective-C,C ++或任何一个家庭中发生奇怪的事情,你永远不会感到惊讶;而是为这么少奇怪的事情保留你的惊喜!

答案 2 :(得分:1)

通过实施Test方法并添加一些简单的日志记录,您可以看到-dealloc何时被取消分配。

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

答案 3 :(得分:1)

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

无论何时你想探索对象生命周期问题,总是使用NSObject的子类来保证行为,并通过覆盖行为轻松地删除日志记录钩子。

答案 4 :(得分:0)

那里没什么奇怪的...... 你需要至少有一个强大的对象引用来保持它的存活。

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