4

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];
jscs
  • 63,694
  • 13
  • 151
  • 195
Boon
  • 40,656
  • 60
  • 209
  • 315
  • Look at the bottom of the page you linked for what `ext_keywordify` is. – rmaddy Dec 09 '15 at 17:41
  • Thanks @rmaddy - missed that. Will re-run things. – Boon Dec 09 '15 at 17:43
  • 3
    Compare http://stackoverflow.com/questions/18599174/how-can-i-use-commercial-at-sign-in-objective-c-macro or http://stackoverflow.com/questions/20861922/meaning-of-objective-c-macros-prefixed-with-an-at-symbol. It is a "trick" to define a macro in such a way that the user *thinks* the macro name starts with @. – Martin R Dec 09 '15 at 17:44
  • Thanks for the links @MartinR. I reformatted my question. – Boon Dec 09 '15 at 18:00

1 Answers1

6

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.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thanks. Question is usually code using weakify continues to refer to self. How does the use of weakify make self point to weak self? – Boon Dec 09 '15 at 18:15
  • @Boon: Sorry, I don't get it. `self_weak_` is initialized to `self`, so `self_weak_` is a weak reference to the same object that `self` points to. – Martin R Dec 09 '15 at 18:23
  • Sorry for the confusion - let me include an example. – Boon Dec 09 '15 at 18:25
  • Added example towards the end of the question. – Boon Dec 09 '15 at 18:29
  • @Boon: In your example, `[[self.obj doSomething];` does *not* refer to the weak pointer. But note that there is another macro `@strongify`, and *that* creates strong local variables with the same name as the original, in your case `self`. – Martin R Dec 09 '15 at 18:31
  • In that case I don't see how weakify(self) is useful. Because strongify is just taking self and make it a strong ref, but self is already a strong ref, no? – Boon Dec 09 '15 at 18:40
  • @Boon: The use of the weak reference is (for example) to prevent retain cycles. If the closure is executed (perhaps at some later time) and the object that the weak reference points to has been deallocated in the meantime then self_weak_ is nil (and the local self variable will be nil as well). – Martin R Dec 09 '15 at 18:55
  • @MartinR: When you pass `self` into `@strongify` inside the closure, doesn't the closure capture it? – dudeman Dec 09 '15 at 20:40
  • @MikeAtNobel: The closure captures only `self_weak_` (which is a weak reference, so it won't prevent deallocation of the referenced object). When the closure is *executed*, a strong *local variable* called "self" is created with `__strong __typeof__(self) self = (self_weak_);` . This is a strong reference to the object which exists only during the execution of the closure, it prevents deallocation *while* the closure is executing. – Martin R Dec 09 '15 at 20:45
  • @MartinR: I guess where I'm confused is in how the closure only captures `self_weak_` when `self_weak_` doesn't appear in the closure, but instead `self` does. – dudeman Dec 09 '15 at 20:52
  • @MikeAtNobel: `__strong __typeof__(self) self` declares a *local variable* which is completely unrelated to (and shadows) the outer `self`. – The intention of these macros is to make life easier, but choosing the same name for the local strong variable can be confusing. With `__weak __typeof__(self) weakSelf = self;` outside of the closure and `__strong __typeof__(self) strongSelf = weakSelf;` inside the closure it may be more clear. – Martin R Dec 09 '15 at 20:56
  • @MartinR: Ok, that clears it up. Thanks for that explanation. – dudeman Dec 09 '15 at 20:58