assign vs weak, __block vs __weak

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

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容