Objective-C - 弱属性 - getter自动释放 (自动引用计数)

4
我对ARC(自动引用计数)中的weak属性有疑问。
我的理解是:weak属性的行为与assign属性类似,只是当该属性所指向的实例被销毁时,ivar会被设置为nil。
问题:
1. 我觉得weak属性的getter方法会retain和autorelease。它不应该像assign属性的getter那样不retain和autorelease吗?(请参考程序)
程序:
以下是我给出的程序实际输出和预期输出。
注意 - 当我将属性从weak改为assign时,达到了我的预期输出。
#import<Foundation/Foundation.h>

@interface A : NSObject
- (void) dealloc;
@end

@implementation A
- (void) dealloc
{
    printf("\tinstance of A deallocated = %p\n", self);
}
@end

@interface B : NSObject
@property (weak) A* xa1;
- (void) dealloc;
@end

@implementation B
@synthesize xa1;
- (void) dealloc
{
    printf("\tinstance of B deallocated = %p\n", self);
}
@end


int main()
{
    B* b1 = [[B alloc] init];

    @autoreleasepool                        //autoreleasepool 1
    {   
        {                                   //block 1
            A* a1 = [[A alloc] init];
            printf("\ta1 = %p\n", a1);

            b1.xa1 = a1; 

            A* a3 = b1.xa1;

            printf("--- end of block 1\n");
        }                                       //at this point i expected instance pointed by a1 to be destroyed

        printf("--- end of autoreleasepool 1\n");
    }   

    printf("---- end of main\n");

    return(0);
}

实际输出:

    a1 = 0x10d713f50
--- end of block 1
--- end of autoreleasepool 1
    instance of A deallocated = 0x10d713f50
---- end of main
    instance of B deallocated = 0x10d713d30

我的期望输出:

    a1 = 0x10d713f50
--- end of block 1
    instance of A deallocated = 0x10d713f50
--- end of autoreleasepool 1
---- end of main
    instance of B deallocated = 0x10d713d30

谢谢

2个回答

4

在属性上提供 weak 假定 ivar 拥有 __weak 所表示的所有权,也就是 @synthesize 的指令。

根据http://clang.llvm.org/docs/AutomaticReferenceCounting.html §4.2,读取 __weak 变量需要保留对象(当然,在使用后要释放):

读取是指对对象值执行左值转换为右值操作。

  • 对于 __weak 对象,当前 pointee 被保留,并在当前完整表达式的结尾处被释放。这必须与赋值和 pointee 的最终释放原子执行。
  • 对于所有其他对象,左值加载具有基本语义。

虽然没有明确解释原因,但可以想象如果你从 __weak 变量获得的对象在你开始使用它之前就已经死了会发生什么。弱引用的目的是确保你拥有一个能够被识别的生命周期的 nil 或有效对象。这就是为什么读取其值意味着保留 pointee(然后属性 getter 方法返回自动释放的对象)。

这不是 Obj-C 独有的,这是所有弱引用实现的常见惯例(包括基于引用计数和垃圾回收的实现)。弱指针不能直接提供指针值,它们必须创建一个强指针来“持有”对象,以确保在调用者开始使用之前不会死亡。在 Obj-C 中,是保留-自动释放;在 C++ 中,weak_ptr 首先创建 shared_ptr,在垃圾回收环境中,返回一个强引用并默默地延长对象的生命周期。


谢谢回复,我有一个疑问。如果我错了,请纠正我 - 使用弱引用的目的是确保生命周期不会增加,只要所有者不销毁它就可以使用它。如果寿命将被延长,那么使用弱指针的目的不就被违反了吗? - user1046037
@user1046037 我已经更新了我的答案,并附上了一个链接,其中明确记录了这种行为。 - hamstergene
@user1046037 在属性上使用 weak 只是 @synthesize 的一条指令。你观察到的行为是由于 __weak 的读取语义造成的:你不能将弱引用转换为 unsafe_unretained 指向对象的指针,因为它的生命周期随时可能结束,你必须在使用之前保留对象。 - hamstergene
我认为 [bar doSomethingThatDestroysFoo] 除非包括fooPtr在内的所有owner都超出作用域,否则无法销毁fooPtr指向的实例。 - user1046037
1
@user1046037 或许那不是一个好的例子。我们可以这样说,由方法(getter)返回的对象必须至少在当前作用域结束之前延长其生命周期,对于弱指针而言,在未先进行保留操作时则不成立。 - hamstergene
非常感谢您耐心的解释。您是正确的,看起来我需要多阅读一些资料。如果我有任何疑问,我会再回来的。再次感谢!! - user1046037

0

x1的获取器看起来像这样:

function -[B xa1] {
    var_8 = rdi;
    var_0 = rsi;
    rdi = objc_loadWeakRetained(var_8 + *_OBJC_IVAR_$_B.xa1);
    rax = [rdi autorelease];
    return rax;
}

所以当你获取属性时

A* a3 = b1.xa1;

a1 == b1.xa1 获取自动释放调用并由自动释放池保留


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接