vaj4tg
Last Updated: February 25, 2016
·
13.79K
· rabovik
7eeed4cd79368adc2148b3519f11b6b9

Making all self references in blocks weak by default

Picture

Problem

Every iOS programmer who have to deal with asynchronous block-based code is familiar with the problem of retain cycles caused by self captured in block:

self.block = ^{
    // Ooops, self is retained by block 
    // which is retained by self.
    // We have a retain cycle here.
    [self doSomeWork];
};

And well-known obvious solution here is using weak references:

__typeof(self) __weak weakSelf = self;
self.block = ^{
    [weakSelf doSomeWork];
};

If you (like me) are tired of seeing dozens of weakSelf variables in your code, may be you share my dream of having all references to self inside any block been weak by default.
If we have a dream, why not to make it come true? At least, just for fun?

Solution

Let me introduce a weakifySelf macros.

self.block = weakifySelf(^{
    // self is weak!
    [self doSomeWork];
});

It takes a block (lets call it a target block) of any signature as an argument and makes all references to self in it weak.
The sources are at github.

How does this magic work?

Lets build this macros step by step.

  1. To have all references to self been weak we need a scope with a self variable declared as weak. The simplest way to create such scope is to declare another block:

    void(^intermediateBlock)(__typeof(self) __weak self) =
        ^(__typeof(self) __weak self) {
            // self is weak in this scope
            ^{
                // self is weak here too
                [self doSomeWork];
            };
        };

    Here we declare an intermediate block with weak self variable as an argument.

  2. Now we need to pass a weak self reference to our intermediate block. So we write a special function which duty is quite simple: make a weak reference from the strong reference and pass it to the block:

    void rs_blockWithWeakifiedSelf(id self,
        void(^intermediateBlock)(id __weak self))
    {
        id __weak weakSelf = self;
        intermediateBlock(weakSelf);
    }
    // …
    rs_blockWithWeakifiedSelf(self,
        ^(__typeof(self) __weak self) {
            ^{
                [self doSomeWork];
             };
        }
    );
  3. OK, now our target block is declared in a scope where self is weak. But our initial goal was to save the block to the variable. So we:

    1) change intermediate block so that it will return our target block;

    2) in function we will return the result of calling intermediate block, i.e. our target block.

    id rs_blockWithWeakifiedSelf(id self,
        id(^intermediateBlock)(id __weak self))
    {
        id __weak weakSelf = self;
        return intermediateBlock(weakSelf);
    }
    // …
    self.block = rs_blockWithWeakifiedSelf(self,
        ^id(__typeof(self) __weak self) {
            return ^{
                [self doSomeWork];
            };
        }
    );
  4. Now all we have to do is move code to macros:

    #define weakifySelf(BLOCK) \
        rs_blockWithWeakifiedSelf(self, \
        ^id(__typeof(self) __weak self) { \
            return BLOCK; \
        })
    // …
    self.block = weakifySelf(^{
        [self doSomeWork];
    });

Extra bonus

Another common task is creating a strong reference from weak:

self.block = weakifySelf(^{
    __typeof(self) __strong strongSelf = self;
    if (strongSelf) {
        // some potentially unsafe work
        strongSelf->_i = 42;
    }
});

And here are two simple macros simplifying routine:

  • strongify(variable) - creates a strong reference to a variable that will shadow the original

    self.block = weakifySelf(^{
        strongify(self);
        if (!self) return;
        self->_i = 42;
    });
  • strongifyAndReturnIfNil(variable)

    self.block = weakifySelf(^{
        strongifyAndReturnIfNil(self);
        self->_i = 42;
    });

So our final code may look like

-(void)someMethod{
    self.block = weakifySelf(^{
        // Self may be nil here
        [self doSomeWork];
        strongifyAndReturnIfNil(self);
        // Self is strong and not nil.
        // We can do ivars dereferencing
        // and other stuff safely
        self->_i = 42;
    });
}

The final strokes

  • Depends of build settings compiler may produce warnings reminding that we shadow variables. To silence them we need to add some pragma directives. See sources for details.

    _Pragma("clang diagnostic ignored \"-Wshadow\"")
  • We still need to be careful and never use instance variables inside blocks directly. For example this will cause a retain cycle:

    self.block = weakifySelf(^{
        // Never do so!
        _i = 42;
    });

    Instead we always must do self dereferencing:

    self.block = weakifySelf(^{
        strongifyAndReturnIfNil(self);
        self->_i = 42;
    });
Say Thanks
Respond

6 Responses
Add your response

8713

weakifySelf(^{ [NSString stringWithFormat:@"commas make me %@", @"sad"]; });

[!] Too many arguments provided to function-like macro invocation

weakifySelf(^{ ([NSString stringWithFormat:@"paran wrapped commas make me %@", @"happy"]); });

over 1 year ago ·
8728
7eeed4cd79368adc2148b3519f11b6b9

@hafthor Excellent point, thanks!
Adding ... to macro declaration fixed this issue:

#define weakifySelf(BLOCK...)
over 1 year ago ·
13307
0 o y3pn61znkol5k7p9v pzq0zblm1 37ykeypqbgotay2f9ftqudtn12rj5lkkh 0ay01pmnjrge

Question: self is the reserve keyword, why it can be argument without problem?

over 1 year ago ·
13313
7eeed4cd79368adc2148b3519f11b6b9

@doon Every method in Objective-C goes in pair with plain C function implementing it. self and _cmd are just the first parameters of each implementation, not the reserved keywords.

over 1 year ago ·
13501
0 o y3pn61znkol5k7p9v pzq0zblm1 37ykeypqbgotay2f9ftqudtn12rj5lkkh 0ay01pmnjrge

Thank you, rabovik. I went through the runtime guide, then I understood.

over 1 year ago ·
15240
1bfbc4cdca2998f2aa56fda6a4052ee1

this is really nice macro but somehow after switching to using it, xcode won't let me debug with breakpoint within the block anymore

over 1 year ago ·