Weak Self in Blocks

前端 未结 3 791
[愿得一人]
[愿得一人] 2021-01-06 23:55

Do I need to check if weak self is nil in blocks?

I create weakSelf pointer like:

__weak typeof(self) weakSelf = self;

and in the b

相关标签:
3条回答
  • 2021-01-07 00:29

    Weak references do not retain the referred object. If none else is retaining it, the object is released and the weak references refers to nil.

    Therefore it is possible that your code is executed with a weakSelf that refers nil. But this is no reason to check for it at all. Especially in Objective-C you use a defined behavior, if you send a message nil. I. e. it is perfect code, if you set a property using a nil reference. It simply goes to nowhere.

    Of course sometime you do not want to interact with nil. In such a case you have to check for it.

    BTW: You need weakSelf only in some, rarely circumstances. It is an urban legend that in closures in general references to self has to be weak to prevent retain cycles. It has not been true, it is not true and it will never be true.

    0 讨论(0)
  • 2021-01-07 00:51

    That check is unnecessary, and is giving you a false sense of security.

    Here's the problem:

    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        if (!weakSelf) { return; }
        // THE LINE OF INTEREST
        [weakSelf doSomething];
    });
    

    At THE LINE OF INTEREST, some other thread might clear the last strong reference to self, at which point weakSelf is set to nil. So the doSomething message gets sent to nil, which is “safe” (it does nothing), but might not be what you expected!

    It's worse if you want to take a different action when weakSelf is nil, e.g.

    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        if (weakSelf) {
            [weakSelf doSomething];
        } else {
            [someOtherObject doSomethingElse];
        }
    });
    

    In this case, between the time the block verifies that weakSelf is not nil and the time it sends the doSomething message, weakSelf might become nil, and neither doSomething nor doSomethingElse will actually run.

    The correct solution is this:

    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        typeof(self) strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf doSomething];
        } else {
            [someOtherObject doSomethingElse];
        }
    });
    

    In this case, copying weakSelf to strongSelf (which is strong by default) is atomic. If weakSelf was nil, strongSelf will be nil. If weakSelf was not nil, strongSelf will not be nil, and will be a strong reference to the object, preventing it from being deallocated before the doSomething message.

    0 讨论(0)
  • 2021-01-07 00:52

    It seems quite unnecessary since calling a message on nil is a no-op. (Nothing happens)

    ^{
        [weakSelf doSomething]; //Does nothing if weakSelf is nil
    }
    

    The only reason I can think you might want to do this is if other messages (not on self) shouldn't be called

    ^{
        // Here I don't want to add weakSelf as an observer if it's nil
        if (!weakSelf) return;
    
        [OtherClass addObserverForSomething:weakSelf];
    }
    
    0 讨论(0)
提交回复
热议问题