Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why __strong __typeof(weakSelf)strongSelf = weakSelf ? #20

Open
onmyway133 opened this issue Jul 22, 2014 · 18 comments
Open

Why __strong __typeof(weakSelf)strongSelf = weakSelf ? #20

onmyway133 opened this issue Jul 22, 2014 · 18 comments

Comments

@onmyway133
Copy link

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 ?

@albertodebortoli
Copy link
Member

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.

@rbsgn
Copy link

rbsgn commented Jul 22, 2014

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.

@rbsgn
Copy link

rbsgn commented Jul 22, 2014

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);
};

@rbsgn
Copy link

rbsgn commented Jul 22, 2014

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

@luca-bernardi
Copy link
Contributor

@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.

@notorca
Copy link

notorca commented Jul 22, 2014

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

@notorca
Copy link

notorca commented Jul 22, 2014

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"); } ;

@albertodebortoli
Copy link
Member

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.

@onmyway133
Copy link
Author

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

@rbsgn
Copy link

rbsgn commented Jul 23, 2014

@lukabernardi thanks for clarification!

@luca-bernardi
Copy link
Contributor

@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

@notorca
Copy link

notorca commented Jul 23, 2014

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

@onmyway133
Copy link
Author

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

@notorca
Copy link

notorca commented Jul 23, 2014

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

@heistings
Copy link

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

@heistings
Copy link

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

@jinguang1
Copy link

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.

@jinguang1
Copy link

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants