iOS 笔记

Block 中何時可以直接用 self,何時必須用 weakSe

2018-05-24  本文已影响0人  Dayon

若 object 本身没有去 retain 这个 block (即没有把这个 block 作成一个 property),则可以直接在 block 中使用 self

//block 不是 property
dispatch_block_t completionBlock = ^{
    // 未 retain block,可直接用 self
    NSLog(@"%@", self);
}
MyViewController *myController = [[MyViewController alloc] init...];
[self presentViewController:myController
                   animated:YES
                 completion:completionHandler];
//把这个 block 作成一个 property
self.completionHandler = ^{
    // 有 retain block,直接用 self 会造成 retain cycle
    NSLog(@"%@", self);
}
MyViewController *myController = [[MyViewController alloc] init...];
[self presentViewController:myController
                   animated:YES
                 completion:self.completionHandler];

当有 retain block 时,应该使用 weakSelf

__weak typeof(self) weakSelf = self;
self.completionHandler = ^{
    // 打破 retain cycle
    NSLog(@"%@", weakSelf);
};

MyViewController *myController = [[MyViewController alloc] init...];
[self presentViewController:myController
                   animated:YES
                 completion:self.completionHandler];

但只用 weakSelf 的问题在于,如果在 block 中必须多次使用到 weakSelf 会有危险,因为在多事件执行时,weakSelf 有可能在 block 跑到一半的时候被设成 nil

__weak typeof(self) weakSelf = self;
dispatch_block_t block =  ^{
    [weakSelf doSomething]; // weakSelf != nil
    // preemption, weakSelf turned nil
    [weakSelf doSomethingElse]; // weakSelf == nil
};

因此必须在 block 内使用 strongSelf,确保 reference 不会执行到一半变成 nil (注意在建立 strongSelf 以后还会再判断其是否为 nil,因为有可能在指定 strongSelf 的时间点 weakSelf = self 就已经为 nil 了)

__weak typeof(self) weakSelf = self;
myObj.myBlock =  ^{
    __strong typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
      [strongSelf doSomething]; // strongSelf != nil
      // preemption, strongSelf still not nil
      [strongSelf doSomethingElse]; // strongSelf != nil
    }
    else {
        // Probably nothing...
        return;
    }
};

总结:

  1. 当 block 不是 property 时,用 self 即可
  2. 当 block 是 property,需使用 weakSelf
  3. 当 block 内会多次使用 weakSelf,且有用到多次执行时,需使用 strongSelf
  4. 并不是所有 block 都得用 weakSelf

事实上大多数的 iOS 原生套件,以及 GCD 的 block 是不会造成 retain cycle 的,因为他们并没有去 retain block

此外也可以因为把 block property 设为 nil 来打破 retain cycle
例如 AFNetworking 就使用了类似的操作方式
因此在 AFNetworking 的 block 中使用 self 也不会造成 retain cycle 问题

另外即使将变量声明成 instance variable(实例变量) 而非 property(属性),在 block 中使用时还是会 retain 到 self 而发生 retain cycle,因为 ivar 其实也是 self 的一部份

@interface MyViewController () {
    NSString *tempStr;
}

self.completionHandler = ^{
    // 这里的 tempStr 相当于 self->tempStr,因此还是会造成 retain cycle
    NSLog(@"%@", tempStr);
}

但 ivar 又无法使用 weakSelf 去取值,因此解決方法有:

 __weak __typeof(self) weakSelf = self;
self.completionHandler = ^{
    // 用 weakSelf->tempStr 是无法取值的
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    NSLog(@"%@", strongSelf->tempStr);
}

更详细说明参照:
https://github.com/oa414/objc-zen-book-cn#-block
http://blog.waterworld.com.hk/post/block-weakself-strongself

上一篇下一篇

猜你喜欢

热点阅读