Search code examples
objective-cmemory-managementmacrosautomatic-ref-countingweak-references

Understanding @weakify macro


How does @weakify work behind the scene? The idea of using @weakify(self) is to have it serve as a shorthand to this:

__weak __typeof__(self) weakSelf = self;

Note: @weakify macro is defined here.

I tried to understand how it works by collecting all the macro that it uses:

#define weakify(...) \
            ext_keywordify \
            metamacro_foreach_cxt(ext_weakify_,, __weak, __VA_ARGS__)

#define ext_weakify_(INDEX, CONTEXT, VAR) \
    CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)

#define metamacro_argcount(...) \
        metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)


#define metamacro_concat(A, B) \
        metamacro_concat_(A, B)

#define metamacro_concat_(A, B) A ## B

#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)

#define metamacro_at(N, ...) \
        metamacro_concat(metamacro_at, N)(__VA_ARGS__)

#define metamacro_head(...) \
        metamacro_head_(__VA_ARGS__, 0)

#define metamacro_head_(FIRST, ...) FIRST

#define metamacro_at0(...) metamacro_head(__VA_ARGS__)
#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)

#if DEBUG
#define ext_keywordify autoreleasepool {}
#else
#define ext_keywordify try {} @catch (...) {}
#endif

I put the entire code above along with below into a .c file:

@weakify(self)

and compile it using gcc -E test.c.

The output I get is:

@try {} @catch (...) {} __attribute__((objc_gc(weak))) __typeof__(self) self_weak_ = (self);

Question:

Given that the generated output from the macro is self_weak_. In normal code using @weakify(self), self is still directly referenced after weakify() call, how does the use of self turn into a weak self given that the code not using self_weak_?

For example, I see this often:

 @weakify(self)
 [[self.obj doSomething];

How does self.obj use weak self? Shouldn't the code be:

 @weakify(self)
 [[self_weak_.obj doSomething];

Solution

  • If you dump the clang predefined macros with

    clang -dM -E -x c /dev/null
    

    then you'll find

    #define __weak __attribute__((objc_gc(weak)))
    

    So if you write __weak in your source this will be expanded to the clang attribute objc_gc(weak). In other words,

    @try {} @catch (...) {} __attribute__((objc_gc(weak))) __typeof__(self) self_weak_ = (self);
    

    is equivalent to

    @try {} @catch (...) {} __weak __typeof__(self) self_weak_ = (self);
    

    Now @try {} @catch (...) {} is a no-op (a trick to define a macro that appears to start with an @ character, compare How can I use commercial at sign in Objective-C macro?), so what is left is

    __weak __typeof__(self) self_weak_ = (self);
    

    which is standard to define and initialize a weak variable self_weak_ pointing the same object that self points to.

    @weakify is meant to be used with its counterpart @strongify, for example:

    @weakify(self);
    // Expands to:
    // __weak __typeof__(self) self_weak_ = (self);
    
    void (^someClosure)(void) = ^void () {
    
        @strongify(self);
        // Expands to:
        // __strong __typeof__(self) self = (self_weak_);
    
        if (self) {
            [self doSomething];
            [self doSomethingElse];
        }
    };
    

    Inside the closure, self is a local variable and initialized as a strong reference to the object if that object still exists. If the object has been deallocated in the meantime (because no strong references to it exist anymore), self_weak_ is nil and the local variable self is also nil.