美文网首页iOS 笔记
Block 中何時可以直接用 self,何時必須用 weakSe

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

作者: Dayon | 来源:发表于2018-05-24 16:43 被阅读0次

    若 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 去取值,因此解決方法有:

    • 1、乖乖建立 property (可能比较简单)
    • 2、使用 weakSelf + strongSelf
     __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

    相关文章

      网友评论

        本文标题:Block 中何時可以直接用 self,何時必須用 weakSe

        本文链接:https://www.haomeiwen.com/subject/memsjftx.html