美文网首页
assign vs weak, __block vs __we

assign vs weak, __block vs __we

作者: IreneWu | 来源:发表于2016-08-25 15:27 被阅读200次

    assign vs weak

    assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。

    assign其实也可以用来修饰对象,那么我们为什么不用它呢?因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。

    而weak修饰的对象在释放之后,指针地址会被置为nil。

    __block vs __weak

    __block:使用__block修饰的变量可以在__block中被修改,且会被retain(MRC下不会retain)

    __weak:使用__weak修饰的变量不会在block代码块中被retain

    同时,在ARC下,要避免block出现循环引用要用:

    __weak__typeof(&*self)weakSelf =self;

    等同于__weakUIViewController*weakSelf =self;

    为什么不用block 是因为通过引用来访问self的实例变量 ,self被retain,block也是一个强引用,引起循环引用,用week是弱引用,当self释放时,weakSelf已经等于nil。

    MRC模式下
    使用__block能够避免引起循环引用的问题

    ARC模式下

    使用__unsafe_unretained 和 __weak都可以避免循环引用的问题,但由于前者是unsafe的,会造成野指针问题,所以尽量少用unsafe_unretained关键字

    另外在多线程环境下(block中的wSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。

    可参考AFNetworking代码:

    __weak__typeof(self)weakSelf =self;

    AFNetworkReachabilityStatusBlock callback =  ^(AFNetworkReachabilityStatus status) {           __strong__typeof(weakSelf)strongSelf = weakSelf;   

    strongSelf.networkReachabilityStatus= status;

    if(strongSelf.networkReachabilityStatusBlock) {       

    strongSelf.networkReachabilityStatusBlock(status);   

    }};

    第一行:__weak __typeof(self)weakSelf = self;

    为防止callback内部对self强引用,weak一下。

    其中用到了__typeof(self),这里涉及几个知识点:

    1. __typeof、__typeof__、typeof的区别

    他们没有区别,在早期C语言中没有typeof这个关键字,__typeof、__typeof__是在C语言的扩展关键字的时候出现的。

    typeof是现代GNU C++的关键字,从Objective-C的根源说,他其实来自于C语言,所以AFNetworking使用了继承自C的关键字。

    2. 对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法,原因如下

    大致说法是老LLVM编译器会将__typeof转义为 XXX类名 *const __strong的__strong和前面的__weak关键字对指针的修饰又冲突了,所以加上&*对指针的修饰。

    第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;

    self转回strong了,这里__typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。

    第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。

    最后第五行,使用前对block判空。


    若 object 本身沒有去 retain 這個 block (即沒有把這個 block 作成一個 property),則可以直接在 block 中使用 self

    dispatch_block_t completionBlock = ^{

        // 未 retain block,可直接用 self

        NSLog(@"%@", self);

    }

    MyViewController *myController = [[MyViewController alloc] init...];

    [self presentViewController:myController animated:YES completion:completionHandler];

    若有 retain block,直接用 self 會造成 retain cycle

    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

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

    }

    相关文章

      网友评论

          本文标题:assign vs weak, __block vs __we

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