iOS中Block的回調(diào)使用和解析詳解
Block 回調(diào)實現(xiàn)
先跟著我實現(xiàn)最簡單的 Block 回調(diào)傳參的使用,如果你能舉一反三,基本上可以滿足了 OC 中的開發(fā)需求。已經(jīng)實現(xiàn)的同學可以跳到下一節(jié)。
首先解釋一下我們例子要實現(xiàn)什么功能(其實是爛大街又最形象的例子):
有兩個視圖控制器 A 和 B,現(xiàn)在點擊 A 上的按鈕跳轉(zhuǎn)到視圖 B ,并在 B 中的textfield 輸入字符串,點擊 B 中的跳轉(zhuǎn)按鈕跳轉(zhuǎn)回 A ,并將之前輸入的字符串
顯示在 A 中的 label 上。也就是說 A 視圖中需要回調(diào) B 視圖中的數(shù)據(jù)。
想不明白的同學可以看一看最終實現(xiàn)的效果圖:
這里不再對 Block 的語法做說明了。
首先,我們需要定義兩個試圖控制器 AViewController
和 BViewController
,現(xiàn)在我們需要思考一下,Block 應該在哪里定義呢?
我們可以簡單地這樣思考,需要回調(diào)數(shù)據(jù)的是 A 視圖,那么 Block 就應該在 B 中定義,用于獲取傳入回調(diào)數(shù)據(jù)。
因此我們在 BViewController.h
中定義如下:
//BViewController.h #import <UIKit/UIKit.h> typedef void(^CallBackBlcok) (NSString *text);//1 @interface BViewController : UIViewController @property (nonatomic,copy)CallBackBlcok callBackBlock;//2 @end
在這里,代碼 1 用 typedef 定義了 void(^) (NSString *text)
的別名為 CallBackBlcok
。這樣我們就可以在代碼 2 中,使用這個別名定義一個 Block 類型的變量 callBackBlock
。
在定義了 callBackBlock
之后,我們可以在 B 中的點擊事件中添加 callBackBlock
的傳參操作:
//BViewController.m - (IBAction)click:(id)sender { self.callBackBlock(_textField.text); //1 [self.navigationController popToRootViewControllerAnimated:YES]; }
這樣我們就可以在想要獲取數(shù)據(jù)回調(diào)的地方,也就 A 的視圖中調(diào)用 block:
// AViewController.m - (IBAction)push:(id)sender { BViewController *bVC = [self.storyboard instantiateViewControllerWithIdentifier:@"BViewController"]; bVC.callBackBlock = ^(NSString *text){ // 1 NSLog(@"text is %@",text); self.label.text = text; }; [self.navigationController pushViewController:bVC animated:YES]; }
代碼 1 中,通過對回調(diào)將 B 中的數(shù)據(jù)傳遞到代碼塊中,并賦值給 A中的 label,實現(xiàn)了整個回調(diào)過程。
上例是通過將 block 直接賦值給 block 屬性,也可以通過方法參數(shù)的方式傳遞 block 塊。
關于 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):
__weak AViewController *weakSelf = self; bVC.callBackBlock = ^(NSString *text){ NSLog(@"text is %@",text); // self.label.text = text; weakSelf.label.text = text; };
第二個問題是我自己對 Block 的理解不到位,我們都知道 Block 能截取自動變量,并且是不能在 Block 塊中進行修改的(除非用__block修飾符),但是很明顯 weakSelf.label.text
的值被修改了,并且沒有用__block
修飾符, 這是為什么呢?因為 label 是個全局變量,而如果像如下的局部變量 a 是不能修改的,編譯器也會報錯:
局部變量
通過這個小例子發(fā)現(xiàn)的兩個問題,也算是值得了。
Block 為什么能實現(xiàn)神奇的回調(diào)
在這里我不會說什么實現(xiàn)原理,僅僅是個人對 Block 能實現(xiàn)神奇回調(diào)的理解,有錯誤的地方請大家指出。
在先前使用 Block 的過程中,雖然會使用,但是總是有一個疑惑,簡單說來就是:
為什么在 A 中的 block 塊能調(diào)用到 B 中的數(shù)據(jù)?
回顧一下我們在 B 中所實現(xiàn)的代碼,不外乎定義了一個 Block 變量,并在適當?shù)臅r候傳入?yún)?shù),那么為什么在調(diào)用了 self.callBackBlock(_textField.text)
之后,值就神奇?zhèn)鞯搅?A 中的 Block 塊了呢?
通過整理使用的過程,我發(fā)現(xiàn)是我們的思維陷入了誤區(qū)(可能是我個人),我們認為在 B 中傳入 _textField.text
參數(shù)之后, A 中的 Block 塊就可以獲取到值。雖然思路是對的,但其實是不完整,導致我們形成了回調(diào)的數(shù)據(jù)是通過某種底層實現(xiàn)傳遞過去的錯覺,這就使得我們認為這不需要深究。
事實是,通過簡單的整理我們可以發(fā)現(xiàn)完整的回調(diào)流程應該是這樣的:
回調(diào)流程
block 代碼塊賦值給 bVC.callBackBlock
,此時 callBackBlock
的指針就指向這個代碼塊。
調(diào)用 callBackBlock(NSString *text)
由于 callBackBlock
的指針是指向 A 中的 block 代碼塊,因此執(zhí)行代碼塊的代碼,實現(xiàn)回調(diào)。
很顯然之前我忽略了代碼塊賦值給 callBackBlock
的這個操作(羞愧)。
現(xiàn)在再通過一段代碼可以更清晰地理解這個原理:
bVC.callBackBlock = ^(NSString *text){ //1 NSLog(@"text is %@",text); }; bVC.callBackBlock = ^(NSString *text){ //2 NSLog(@"text b is %@",text); };
上述代碼中,我們對 callBackBlock
進行了兩次賦值,結果會怎么樣呢?
two block
可以看出來,Block 的回調(diào)只對代碼 2 生效,因為callBackBlock
的指針最后指向了代碼 2 的代碼塊。所以并沒有什么神奇的魔法,也沒什么隱藏的底層機制(這里指的是方便理解的底層)讓你可以帶著疑惑去使用它。
總結
我這個人學習方法,總結起來就是看到新技術,先在自己的代碼里跑一遍,能跑通,并且使用起來沒有什么難度,就基本不會深究了。但是自我反思過,這樣的學習方法是很不對的,寫代碼不能不求甚解,如果想要有所突破,不想局限于碼農(nóng),一定要深入探究一下實現(xiàn)的機制,最起碼要保證不帶著疑惑去使用。以上就是這篇文章的全部內(nèi)容,希望能對大家的學習或者工作帶來一定的幫助,如果有疑問大家可以留言交流。
相關文章
iOS App開發(fā)中的UIStackView堆疊視圖使用教程
UIStackView是iOS9以來新增加的組件,使我們能夠?qū)IView子類對象進行靈活排版,這里我們就來看一下iOS App開發(fā)中的UIStackView堆疊視圖使用教程2016-07-07iOS關鍵字static extern const使用示例詳解
這篇文章主要為大家介紹了iOS關鍵字static extern const使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11