objc-zen/objc-zen-book

Why __strong __typeof(weakSelf)strongSelf = weakSelf ?

Opened this issue · 18 comments

I know this project is about coding convention, but you tried to explain block and its usage, so I ask

In the "Block" section,

__weak __typeof(self)weakSelf = self;
__strong __typeof(weakSelf)strongSelf = weakSelf;

Why typeof(weakSelf) instead of typeof(self)? Aren't they the same thing ?

Unfortunately they are not. Just referencing 'self' within a block will cause the block to retain it and in this case, it will cause a retain cycle (because you know... self would be captured within the block).
The whole thing of weakify and strongify would be leaked just because of this misprint!
It is common to mistype self instead of strongSelf (or weakSelf) within blocks: I suggest to turn on "Implicit retain of 'self' within blocks" in Build Settings (CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF).
This way, the compiler will help you spotting the wrong usages.
Thanks for the question @onmyway133, will add a paragraph on this.

I'm just curious: how one can reproduce this issue after enabling CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF? I tried following steps:

  1. Create empty app with Xcode 6 beta 3
  2. Enable CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF in project build settings
  3. Add following code in -application:didFinishLaunchingWithOptions: and run app in iOS Simulator
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://ya.ru"]]
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                               NSLog(@"%@", self.window);
                           }];

Clang produced no warnings.

Ha! I've found a sample code how to reproduce this issue with implicit retain. However, using __typeof(self) didn't cause compiler warning:

__weak __typeof(self) weakSelf = self;
self.block = ^void() {
    __strong __typeof(self) strongSelf = weakSelf;
    NSLog(@"%@", strongSelf.array);
};

Still can't figure out why compiler ignored retain cycle in +sendSynchronousRequest:... though.

@nskboy you have a retain cycle only if your class is holding a strong reference to the NSURLConnection. Given that is a completion block, this means that you need to keep it alway only for the duration of the request, if the class (NSURLConnection in this case) internally nil-out the strong reference to completion block you don't have a retain cycle.

__typeof(self) can't create a retain cycle because __typeof is complie-time-only operator.

On other hand LANG_WARN_OBJC_IMPLICIT_RETAIN_SELF can't find all possible cases with retain cycle.
For example it will be a warning in this case:
self.block = ^{ NSLog(@"%@", [self description]); };
but not in this:
self.block = self ? ^{ NSLog(@"%@", [self description]); } : ^{ NSLog(@"someStrigng"); } ;

Thanks @notorca, apologise for the big mistake. You're absolutely right:
__strong typeof(self) strongSelf = weakSelf; is perfectly fine within blocks.

And sure, CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF doesn't find all retain cycles, just makes explicit the implicit retain of self (as the name suggests). The current version of CLANG doesn't go too much deep with the analysis.

Thanks, so they are the same :)

Some people ask me these questions

  1. Why using weakSelf solves the problem? (Why the block know not to increase the reference count for that self) ?
  2. Why introducing strongSelf inside block does not increase the reference count for self ?

I collect the answers in UNDERSTANDING WEAK SELF AND STRONG SELF, hope you can take a look at it

@lukabernardi thanks for clarification!

@onmyway133 the reason about the use of weakSelf and strongSelf are also explained in the book https://github.com/objc-zen/objc-zen-book#retain-cycles-on-self

There is another problem, self can be implicitly captured in the block if ivar is accessed.

self.block = ^{ NSLog(@"%d", _ivarInteger); }; 
// self is captured here, same as self->_ivarInteger

To avoid this I created macro:

self.block = @weakself(^(NSObject *argument)) { NSLog(@"%@", [self description]); } @weakselfend;
 // self in block body is strong reference, but until block is called it is weak 

And for cases with ivar:

self.block = @weakselfnotnil(^) { NSLog(@"%@", self->_ivarInteger); } @weakselfend;
 // If self have been released block body is not called

In debug version this macro also checks for "accidental" capturing of the self throw ivars and asserts in runtime when block is created. You can find it here: https://gist.github.com/notorca/9192459#file-weakself-h

There are more subtle ways in which self is referenced inside block, like NSAssert http://sealedabstract.com/code/nsnotificationcenter-with-blocks-considered-harmful/

@onmyway133 yes, my macro also handles situation with NSAssert.

@onmyway133 [NSNotificationCenter defaultCenter] is still on the main thread, there is no need to do this, I think.

@onmyway133 But doing this, you will never cause a retain cycle, unless you are sure about that.

I've error something like this.

File Name is xxxx.m
code : __weak typeof(self) weakSelf = self;
error: expect ; at end of declaration

Anyone help me.

Just I found solution .

replace __weak typeof(self) weakSelf = self;
to __weak __typeof(self) weakSelf = self;

__weak __typeof(self) weakSelf = self; and
__weak xxx *weakSelf = self.
Aren't they the same thing?