Why weak property of associated object is not nilled out if I call its getter?

104 Views Asked by At

Though it's kind of stupid in 2020 that I'm still asking question about ObjC, please be patient and considerate...

I'm reading the source code of BloksKit and ran into a weird situation.

#import <objc/runtime.h>

@interface _WeakAssociatedObjectWrapper : NSObject
@property (nonatomic, weak) id object;
@end

@implementation _WeakAssociatedObjectWrapper
@end

@interface NSObject (AddWeak)
@end

@implementation NSObject (AddWeak)
- (void)setWeakProp:(id)weakProp {
    _WeakAssociatedObjectWrapper *wrapper  = objc_getAssociatedObject(self, @selector(weakProp));
    if (!wrapper) {
        wrapper = [[_WeakAssociatedObjectWrapper alloc] init];
        objc_setAssociatedObject(self, @selector(weakProp), wrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    wrapper.object = weakProp;
}
- (id)weakProp {
    id value = objc_getAssociatedObject(self, _cmd);
    if ([value isKindOfClass:_WeakAssociatedObjectWrapper.class]) {
        return [(_WeakAssociatedObjectWrapper *)value object];
    }
    return value;
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
        {
            NSObject *prop = [[NSObject alloc] init];
            [obj setWeakProp:prop];

            [obj weakProp]; // *Weird!!
        }
        NSLog(@"Now obj.weakProp = %@", [obj weakProp]);
    }
    return 0;
}

This code is adding a weak associated object for category.(BlocksKit does so)

Note the *Weird!! line. If this line is commented out, then it prints (null), which is reasonable since prop is deallocated outside the {} scope. On the other side, if not commented out, it prints <NSObject: 0xxxxx>, which indicates that prop is somehow retained by someone(Or any other reason?). What is happening here??! (BlocksKit behaves the same!)

Environment: XCode 10.3

1

There are 1 best solutions below

0
Asperi On

This is a feature. For the case (and any similar)

[obj weakProp];

by properties/accessors naming convention ARC returns autoreleased instance, so in your case @autoreleasepool holds it and testing as below can show this.

int main(int argc, const char * argv[]) {
    NSObject *obj = [[NSObject alloc] init];
    @autoreleasepool {
        {
            NSObject *prop = [[NSObject alloc] init];
            [obj setWeakProp:prop];

            [obj weakProp]; // *Weird!!
        }
        NSLog(@"Now obj.weakProp = %@", [obj weakProp]);
    }
    NSLog(@"After autoreleased >> obj.weakProp = %@", [obj weakProp]);
    return 0;
}