Objective-C - weak property - getter autoreleases

2019-04-10 19:56发布

问题:

I have a doubt regarding weak property in ARC (auto reference counting)

My understanding (correct me if I am wrong):

weak property behaves similar to the assign property except that when the instance that the property was pointing to gets destroyed, the ivar is made to point to nil.

Question:

  1. I just feel that the getter of the weak property retains and autoreleases. Isn't it suppose to behave like getter of the assign property where the getter doesn't retain and autorelease ?(pls refer to the program)

Program:

I have given below the program with the actual output and my expected output.

Note - When I change property from weak to assign my expected output is met

#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);
}

Actual Output:

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

My Expected Output:

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

Thanks

回答1:

Supplying weak on a property assumes __weak ownership for the ivar, i.e. it's just an instruction for @synthesize.

According to http://clang.llvm.org/docs/AutomaticReferenceCounting.html §4.2, reading __weak variable requires retaining the object (and releasing after, of course):

Reading occurs when performing a lvalue-to-rvalue conversion on an object lvalue.

  • For __weak objects, the current pointee is retained and then released at the end of the current full-expression. This must execute atomically with respect to assignments and to the final release of the pointee.
  • For all other objects, the lvalue is loaded with primitive semantics.

It does not say why, but think of what happens if the object you got from __weak variable dies before you even started using it. The purpose of weak pointer is to make sure you have either nil or a valid object with well-known lifetime, that is why reading its value implies retaining the pointee (and then property's getter returns it autoreleased).

This is not unique to Obj-C, it's common idiom to all weak pointer implementations (both refcounted and garbage collected). Weak pointers can not give out pointer value directly, they have to create strong pointer to 'hold' object to make sure it won't die before caller has even started using it. In Obj-C, it's retain-autorelease; in C++, weak_ptr creates shared_ptr first, in garbage-collected environments, a strong reference is returned and the object's lifetime is prolonged silently.



回答2:

Getter for x1 looks like:

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

So when you get property in

A* a3 = b1.xa1;

a1 == b1.xa1 get autorelease call and retained by autoreleasepool