Block是一種匿名函數(shù),也是一種Objective-C對象券盅。
語法
^ 返回值 (參數(shù)列表) 表達式
^ int (int a) {return a+1}
返回值和參數(shù)列表都可以省略
^ 表達式
^{NSLog(@"abc")}
聲明
block可以作為一個臨時變量鞭执,也可作為方法上的參數(shù),更可以作為一個函數(shù)定義伴找。
// 臨時變量 -
// 返回值類型 (^變量名) (參數(shù)列表) = 語法
int (^aBlock)(int) = ^ int (int a) {return a+1};
NSLog(@"%d", aBlock(10));
// 方法參數(shù) -
(返回值 (^) (參數(shù)列表)) 參數(shù)名
- (void)excute:(void (^) (int a))blk;
// 函數(shù) -
typedef 返回值類型 (^函數(shù)名) (參數(shù)列表)
typedef int (^MyBlock)(int a);
MyBlock blk;
- (void)excute:(MyBlock)blk;
作為變量和作為方法參數(shù)Block的格式有一點區(qū)別盈蛮,為了避免記憶上的麻煩,建議使用函數(shù)形式定義技矮。
局部變量
函數(shù)中的局部變量在block中使用時抖誉,其值不受后續(xù)代碼影響。
int a = 10;
void (^MyBlock)() = ^{
NSLog(@"內(nèi)部 a=%d", a); // 輸出 10
a = 30; // 編譯器報錯
};
a = 20;
MyBlock();
NSLog(@"外部 a=%d", a); // 輸出20
因為block在編譯時會轉(zhuǎn)化為普通的C語言代碼衰倦,使用了struct結(jié)構(gòu)袒炉。在block中使用變量a時,其實質(zhì)是在block中聲明了相同屬性的變量樊零,姑且稱之為copyA我磁,并且將a的值賦予了copyA。在block使用的實際上是copyA。a的值在聲明了block之后才發(fā)生改變夺艰,按代碼的編譯順序并不會影響copyA芋哭,因此輸出的值還是10。
__block
通常情況下block中不允許對外部局部變量重新賦值劲适,除非該變量是靜態(tài)局部變量楷掉、靜態(tài)全局變量或者成員變量。如果需要改變局部變量的值霞势,可以采用__block修飾符進行修飾烹植。
__block int a = 10;
void (^MyBlock)() = ^{
NSLog(@"內(nèi)部 a=%d", a); // 輸出 20
a = 30;
};
a = 20;
MyBlock();
NSLog(@"外部 a=%d", a); // 輸出30
究其原因,使用__block修飾符修飾的變量在編譯時愕贡,block會為其創(chuàng)建一個結(jié)構(gòu)體草雕,結(jié)構(gòu)體中保留了該變量的地址。在使用該變量時固以,實際使用的是指針的方式訪問墩虹,因此不管是在函數(shù)中或者是在block中改變該變量都會互相影響。
strong / copy
當(dāng)block被作為一個成員變量時憨琳,該使用strong還是copy呢诫钓?
在block轉(zhuǎn)換成結(jié)構(gòu)體實例時會使用到objc_retainBlock函數(shù),而該方法在runtime時實際上就是_Block_copy函數(shù)篙螟,因此使用copy即可菌湃。
使用copy方法會將block從棧上復(fù)制到堆上,因此當(dāng)棧上的block被廢棄時(超出作用域)還能繼續(xù)使用該block遍略。
循環(huán)引用
在block中使用某個對象時惧所,block會持有該對象,在內(nèi)部形成一個類似autorelease的對象绪杏。當(dāng)block作為局部變量時下愈,在內(nèi)部使用了self并不會引起循環(huán)引用。但當(dāng)block作為成員變量時蕾久,由于self持有了該block势似,而block又持有了self,就會導(dǎo)致循環(huán)引用腔彰,無法釋放內(nèi)存叫编。因此如果需要在block中使用某個對象,通常建議使用該對象的弱引用霹抛。
BLK blk = ^{
NSLog(@"self=%@", self); // 不會造成循環(huán)引用
};
blk();
self.blk2 = ^{
NSLog(@"self=%@", self); // 循環(huán)引用,編譯器警告
};
self.blk2();
__weak ViewController * weakSelf = self;
self.blk3 = ^{
NSLog(@"self=%@", weakSelf); // 不會引起循環(huán)引用
};
self.blk3();