Block 的本質(zhì)是可以截取自動變量的匿名函數(shù)。
一.block的三種定義方式
1.返回值類型(^block的名字)(參數(shù)類型) = ^(參數(shù)類型和參數(shù)名) {};
void(^block)() = ^(){
NSLog(@"調(diào)用了block");
};
2.//返回值類型(^block的名字)(參數(shù)類型) = ^(參數(shù)類型和參數(shù)名) {};
//如果有參數(shù),定義的時候,必須要寫參數(shù),而且必須要有參數(shù)變量名
int(^block)(int) = ^(int a){
return 1;
};
3.系統(tǒng)提供了一個定義block的宏
returnType(^blockName)(parameterTypes) = ^(parameters) {
//? ? ? ? statements
//? ? };
二.block類型
@property (nonatomic, strong) void(^block)();//這樣在類中可以拿到self.block
//當(dāng)然也可以取別名:
typedef void(^BlockType)();//BlockType不是變量名,而是這種類型的block的別名
//然后就可以這樣
@property (nonatomic, strong) BlockType block;
三.block變量的傳遞
(1)如果是局部變量,Block是值傳遞
(2)如果是靜態(tài)變量,全局變量,__block修飾的變量,block都是指針傳遞
@property (nonatomic, strong) CompletionBlock zfbBlock;
//block只能使用strong,不要使用copy
//因?yàn)楫?dāng)使用copy的時候,set方法是調(diào)用了copy幫你深拷貝一次,沒有這個必要.
//就像NSString一樣,他一般都是@"a"這種常量,沒必要再去深拷貝一次,所以NSString常量也用strong不用copy.
__weak typeof (&*self) weakSelf= self;
/*
因?yàn)閎lock會給內(nèi)部的強(qiáng)指針對象進(jìn)行一次強(qiáng)引用,比如常見的傳入block中的self進(jìn)行強(qiáng)引用
并且在self中,block又是strong的,self對block是強(qiáng)引用
所以,你強(qiáng)引用我,我強(qiáng)引用你,誰也不會被釋放,就造成了循環(huán)引用
所以,為了避免循環(huán)引用,我們要在block使用self之前,進(jìn)行這一步操作:
在block中使用weakSelf,就不會產(chǎn)生循環(huán)引用問題了.
使用了__weak修飾符的對象,作用等同于定義為weak的property。自然不會導(dǎo)致循環(huán)引用問題.
*/
Block 回調(diào)實(shí)現(xiàn)
首先解釋一下我們例子要實(shí)現(xiàn)什么功能(其實(shí)是爛大街又最形象的例子):
有兩個視圖控制器 A 和 B,現(xiàn)在點(diǎn)擊 A 上的按鈕跳轉(zhuǎn)到視圖 B ,并在 B 中的textfield 輸入字符串,點(diǎn)擊 B 中的跳轉(zhuǎn)按鈕跳轉(zhuǎn)回 A ,并將之前輸入的字符串
顯示在 A 中的 label 上。也就是說 A 視圖中需要回調(diào) B 視圖中的數(shù)據(jù)。
我們可以簡單地這樣思考,需要回調(diào)數(shù)據(jù)的是 A 視圖,那么 Block 就應(yīng)該在 B 中定義,用于獲取傳入回調(diào)數(shù)據(jù)。
因此我們在 BViewController.h 中定義如下:
//BViewController.h#import
typedefvoid(^CallBackBlcok(NSString*text);
//1@interfaceBViewController:UIViewController
@property(nonatomic,copy)CallBackBlcok callBackBlock;//2
@end
在這里,代碼 1 用 typedef 定義了void(^) (NSString *text)的別名為CallBackBlcok。這樣我們就可以在代碼 2 中,使用這個別名定義一個 Block 類型的變量callBackBlock。
在定義了callBackBlock之后,我們可以在 B 中的點(diǎn)擊事件中添加callBackBlock的傳參操作:
//BViewController.m
- (IBAction)click:(id)sender {
self.callBackBlock(_textField.text);//1[
self.navigationControllerpopToRootViewControllerAnimated:YES];}
這樣我們就可以在想要獲取數(shù)據(jù)回調(diào)的地方,也就 A 的視圖中調(diào)用 block:
// AViewController.m
- (IBAction)push:(id)sender {?
? BViewController *bVC = [self.storyboardinstantiateViewControllerWithIdentifier:@"BViewController"];? ? bVC.callBackBlock= ^(NSString*text){
// 1NSLog(@"text is %@",text);
self.label.text= text;? ?
};? ?
[self.navigationControllerpushViewController:bVC animated:YES];}
代碼 1 中,通過對回調(diào)將 B 中的數(shù)據(jù)傳遞到代碼塊中,并賦值給 A
中的 label,實(shí)現(xiàn)了整個回調(diào)過程。
上例是通過將 block 直接賦值給 block 屬性,也可以通過方法參數(shù)的方式傳遞 block 塊。
關(guān)于 Block 的疑惑
到目前為止,一切看起來都很美好(如果你照著上面的例子做的話),功能正常, A 視圖中也獲取到數(shù)據(jù)了。但是某些人可能就要說了,你的代碼有問題,你的思路有問題,你這是誤人子弟。
是的,代碼的確還有問題,第一個問題就是循環(huán)引用的問題,在 A 視圖的block 代碼塊中:
bVC.callBackBlock= ^(NSString*text){NSLog(@"text is %@",text);self.label.text= text;? ? ? ? ? };
代碼self.label.text = text;,在 Block 中引用 self ,也就是 A ,而 A 創(chuàng)建并引用了 B ,而 B 引用callBackBlock,此時就形成了一個循環(huán)引用,而編譯器也不會報任何錯誤,我們需要非常小心這個問題(面試百分百問到我會亂說?)。此時我們通常的解決方法是使用弱引用來解除這個循環(huán):
__weakAViewController*weakSelf =self;? ? bVC.callBackBlock= ^(NSString*text){NSLog(@"text is %@",text);//? ? ? ? self.label.text = text;weakSelf.label.text= text;? ? };
第二個問題是我自己對 Block 的理解不到位,我們都知道 Block 能截取自動變量,并且是不能在 Block 塊中進(jìn)行修改的(除非用__block修飾符),但是很明顯weakSelf.label.text的值被修改了,并且沒有用__block修飾符, 這是為什么呢?因?yàn)閘abel是個全局變量,而如果像如下的局部變量a是不能修改的,編譯器也會報錯:
局部變量
通過這個小例子發(fā)現(xiàn)的兩個問題,也算是值得了。