這個(gè)小系列是從 "Zen and the Art of the Objective-C Craftsmanship"中 進(jìn)行的摘抄斜做,共分成6篇箭启,大部分是講代碼風(fēng)格及美化,偶爾看看也不錯(cuò)。
- 原文GitHub地址:
https://github.com/objc-zen/objc-zen-book - 中文版GitHub地址:
https://github.com/oa414/objc-zen-book-cn
Block
關(guān)于block在之前的文章中提到過(guò)芹务,這里就不多說(shuō)了择份。
一些關(guān)鍵點(diǎn):
block 是在棧上創(chuàng)建的
block 可以復(fù)制到堆上
Block會(huì)捕獲棧上的變量(或指針)狸吞,將其復(fù)制為自己私有的const(變量)胆描。
(如果在Block中修改Block塊外的)棧上的變量和指針,那么這些變量和指針必須用__block關(guān)鍵字申明(譯者注:否則就會(huì)跟上面的情況一樣只是捕獲他們的瞬時(shí)值)醋粟。
如果 block 沒(méi)有在其他地方被保持靡菇,那么它會(huì)隨著棧生存并且當(dāng)棧幀(stack frame)返回的時(shí)候消失。僅存在于棧上時(shí)米愿,block對(duì)對(duì)象訪(fǎng)問(wèn)的內(nèi)存管理和生命周期沒(méi)有任何影響厦凤。
如果 block 需要在棧幀返回的時(shí)候存在,它們需要明確地被復(fù)制到堆上育苟,這樣较鼓,block 會(huì)像其他 Cocoa 對(duì)象一樣增加引用計(jì)數(shù)。當(dāng)它們被復(fù)制的時(shí)候违柏,它會(huì)帶著它們的捕獲作用域一起博烂,retain 他們所有引用的對(duì)象。
如果一個(gè) block引用了一個(gè)棧變量或指針漱竖,那么這個(gè)block初始化的時(shí)候會(huì)擁有這個(gè)變量或指針的const副本脖母,所以(被捕獲之后再在棧中改變這個(gè)變量或指針的值)是不起作用的。(譯者注:所以這時(shí)候我們?cè)赽lock中對(duì)這種變量進(jìn)行賦值會(huì)編譯報(bào)錯(cuò):Variable is not assignable(missing __block type specifier)闲孤,因?yàn)樗麄兪歉北径沂莄onst的.具體見(jiàn)下面的例程)。
當(dāng)一個(gè) block 被復(fù)制后烤礁,__block 聲明的棧變量的引用被復(fù)制到了堆里讼积,復(fù)制完成之后,無(wú)論是棧上的block還是剛剛產(chǎn)生在堆上的block(棧上block的副本)都會(huì)引用該變量在堆上的副本脚仔。
...
CGFloat blockInt = 10;
void (^playblock)(void) = ^{
NSLog(@"blockInt = %zd", blockInt);
};
blockInt ++;
playblock();
...
//結(jié)果為:blockInt = 10
self的循環(huán)引用
當(dāng)使用代碼塊和異步分發(fā)的時(shí)候勤众,要注意避免引用循環(huán)。
// 這樣做
__weak __typeof(self) weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
[weakSelf doSomethingWithData:data];
}];
// 不要這樣
[self executeBlock:^(NSData *data, NSError *error) {
[self doSomethingWithData:data];
}];
多個(gè)語(yǔ)句的例子:
// 這樣做
__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomethingWithData:data];
[strongSelf doSomethingWithData:data];
}
}];
// 不要這樣
__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
[weakSelf doSomethingWithData:data];
[weakSelf doSomethingWithData:data];
}];
你應(yīng)該把這兩行代碼作為 snippet 加到 Xcode 里面并且總是這樣使用它們鲤脏。
__weak __typeof(self)weakSelf = self;
__strong __typeof(weakSelf)strongSelf = weakSelf;
在使用block的時(shí)候们颜,分三種情況:
-
直接在 block 里面使用關(guān)鍵詞 self
在這種情況下吕朵,首先要看這個(gè)block是什么樣的。
dispatch_block_t completionBlock = ^{ NSLog(@"%@", self); } MyViewController *myController = [[MyViewController alloc] init...]; [self presentViewController:myController animated:YES completion:completionHandler];
在這種情況下窥突,在block中直接使用self并沒(méi)有問(wèn)題努溃。因?yàn)樵赽lock中對(duì)self進(jìn)行引用,但是self并沒(méi)有保留這個(gè)block阻问。
但是如果是下面這種:
self.completionHandler = ^{ NSLog(@"%@", self); } MyViewController *myController = [[MyViewController alloc] init...]; [self presentViewController:myController animated:YES completion:self.completionHandler];
在block里面引用了self梧税,而這個(gè)block也被self保留(這個(gè)block是一個(gè)屬性),那么會(huì)造成引用循環(huán)称近。解決方法就是使用__weak第队。
-
在 block 外定義一個(gè) __weak 的 引用到 self,并且在 block 里面使用這個(gè)弱引用刨秆。
這樣會(huì)避免循壞引用凳谦,也是通常情況下我們的block作為類(lèi)的屬性被self retain 的時(shí)候會(huì)做的。
__weak typeof(self) weakSelf = self; self.completionHandler = ^{ NSLog(@"%@", weakSelf); }; MyViewController *myController = [[MyViewController alloc] init...]; [self presentViewController:myController animated:YES completion:self.completionHandler];
這個(gè)情況下self在屬性里面 retain 了 block衡未,但是block 沒(méi)有 retain self尸执。所以這樣我們能保證了安全的訪(fǎng)問(wèn) self。
-
在 block 外定義一個(gè) __weak 的 引用到 self眠屎,并在在 block 內(nèi)部通過(guò)這個(gè)弱引用定義一個(gè) __strong 的引用剔交。
和并發(fā)執(zhí)行有關(guān)。當(dāng)涉及異步的服務(wù)的時(shí)候改衩,block 可以在之后被執(zhí)行岖常,并且不會(huì)發(fā)生關(guān)于 self 是否存在的問(wèn)題。(關(guān)于這個(gè)我目前為止并不是很明白葫督,希望有明白的朋友多多指教竭鞍。)
所有文章
【objc-zen-book】1.條件語(yǔ)句&Case語(yǔ)句的注意
【objc-zen-book】2.命名
【objc-zen-book】3.類(lèi)
【objc-zen-book】4.Category & NSNotification
【objc-zen-book】5.美化代碼 & 代碼組織
【objc-zen-book】6.Block & self的循環(huán)引用