Making all self references in blocks weak by default
![Picture][image]
Problem
Every iOS programmer who have to deal with asynchronous block-based code is familiar with the [problem of retain cycles][progWithBlocks] 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][gist].
How does this magic work?
Lets build this macros step by step.
-
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.
-
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]; }; } );
-
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]; }; } );
-
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][gist] 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; });
[gist]: https://gist.github.com/4707815
[progWithBlocks]: http://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW16
[image]: https://coderwall-assets-0.s3.amazonaws.com/uploads/picture/file/1234/56944.png
[statements]:http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
Written by Yan
Related protips
6 Responses
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"]); });
Question: self is the reserve keyword, why it can be argument without problem?
@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.
Thank you, rabovik. I went through the runtime guide, then I understood.
this is really nice macro but somehow after switching to using it, xcode won't let me debug with breakpoint within the block anymore