淺談ios之block的基礎

在iOS 4.0之后,block橫空出世,它本身封裝了一段代碼并將這段代碼當做變量,通過block()的方式進行回調。這不免讓我們想到在C函數中,我們可以定義一個指向函數的指針并且調用:

bool?executeSomeTask(void)?{

//do?something?and?return?if?success?or?not

}

bool?(*taskPoint)(void);

taskPoint?=?something;

上面的函數指針可以直接通過(*taskPoint)()的方式調用executeSomeTask這個函數,這樣對比block跟似乎C語言的函數指針是一樣的,但是兩者仍然存在以下區別:

block的代碼是內聯的,效率高于函數調用

block對于外部變量默認是只讀屬性

block被Objective-C看成是對象處理

小結:

1. ?block 是 C 語言的

2. block 是一種匿名函數

3. 是一種數據類型,可以當作參數傳遞

4. 是一組預先準備好的代碼,在需要的時候執行

Block 應用場景:

1. 自定義視圖的反向傳值

2. Modal / POP 控制器的反向傳值

3. 異步方法執行完畢后的反向傳值

注:

1. Block 的反向傳值一般被稱為回調

2. 反向傳遞的樹枝通過 Block 的參數傳遞

Block 的定義和匿名函數

定義 block 的函數體

1. 以^起始,表示是一個block

2. 以{}包裝block中的代碼塊

^ {

NSLog(@"hello block");? ?

};

提示:Expression result unused=> 表達式沒有使用

使用一個變量記錄 block

// block 是一個沒有參數,沒有返回值的匿名函數

// myBlock 是記錄這個匿名函數的變量名

void(^myBlock)() = ^ {

NSLog(@"hello block");? ?

};

提示:Unused variable 'myBlock'=>'myBlock' 沒有使用

使用變量

// block 是一個沒有參數,沒有返回值的匿名函數

// myBlock 是記錄這個匿名函數的變量名

void(^myBlock)() = ^ {

NSLog(@"hello block");? ??

};

// 執行 myBlock 變量中記錄的代碼myBlock();

Block 的定義可以借助inlineBlock速記,但是:

1. 不要過份依賴inlineBlock

2. block的手寫定義一定要過關!

當做參數傳遞 Block

定義一個接收并且執行 block 的方法

// 接收并且執行 block

- (void)callBlock:(void(^)())completion {

NSLog(@"干點什么");? ??

completion();

}

定義 block 并且當做參數傳遞

#pragma mark - Block 當做參數傳遞

- (void)blockDemo2 {

void(^myBlock)() = ^ {

NSLog(@"hello block");? ??

};? ??

[selfcallBlock:myBlock];

}

通過 Block 的參數回調

準備一個方法,模擬回調網絡請求結果

// ?通過 block 的參數回調模擬網絡請求的結果

- (void)callBlock2:(void(^)(NSString*result))completion {

NSLog(@"網絡加載了一點數據");

NSString*jsonString =@"我是網絡加載的 json 字符串";? ??

completion(jsonString);

}

定義 block 并且傳遞參數

#pragma mark - Block 通過參數回調

- (void)blockDemo3 {

// 1. 定義 blockvoid(^myBlock)(NSString*) = ^ (NSString*json) {

NSLog(@"%@", json);

? ? };

// 2. 調用方法

[selfcallBlock2:myBlock];

}

block的反向傳值

來一個小demo:

點擊PUSH按鈕在最右側控制器輸入用戶名

點擊保存按鈕 POP 控制器,并且將用戶名顯示在nameLabel中

項目準備

右側的 DemoViewController

在DemoViewController中定連線屬性

@interface DemoViewController()

@property(weak,nonatomic)IBOutlet UITextField *nameText;

@end

連線方法

/// 保存并返回

- (IBAction)save:(id)sender {

// 將輸入內容傳回

// 導航控制器彈棧

[self.navigationController popViewControllerAnimated:YES];

}

在 .h 中定義屬性

/// 輸入完成 block 回調

@property(nonatomic,copy)void(^inputCompletion)(NSString*userName);

修改 save 方法

// 保存并返回

- (IBAction)save:(id)sender {

// 判斷 block 屬性是否有內容

if(self.inputCompletion != nil) {

// 執行完成回調將輸入內容傳回

self.inputCompletion(_nameText.text);

? ? }

// 導航控制器彈棧

[self.navigationController popViewControllerAnimated:YES];

}

左側的 viewController

導入頭文件

#import"DemoViewController.h"

屬性連線

@property(weak,nonatomic)IBOutlet UILabel*nameLabel;

實現prepareForSegue:

- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender {

// 1. 獲取目標控制器

DemoViewController *vc = segue.destinationViewController;

// 2. 設置完成回調

[vc setInputCompletion:^(NSString*name) {

self.nameLabel.text= name;

? ? }];

}

與代理的對比

實現步驟的對比

被調用方(子控制器)

代理

定義協議

定義代理屬性

需要的時候通知代理執行協議方法

block

定義 block 屬性

需要的時候執行 block 屬性

調用方

代理

設置代理

遵守協議

實現協議方法

block

設置 block 屬性,準備需要執行的代碼

代碼實現的對比

Block 的優勢

使用 block 所有的代碼寫在一起,更加便于維護和閱讀

使用 代理 代碼想對松散

Block 的弱勢

一個回調對應一個屬性,屬性定義在類的頭文件中,容易和其他屬性混在一起

代理設計模式通過協議預先定義好代理的行為,從設計模式而言,更加嚴謹

尤其在協議方法很多的時候,使用代理更加容易上手!

使用 block 需要注意循環引用!

block 保存的內存區域

不引用任何外部變量的 block

- (void)blockDemo1 {

void(^myBlock)() = ^ {

NSLog(@"hello world");

? ? };

NSLog(@"%@", myBlock);

}

不引用任何外部變量的 block 保存在全局區__NSGlobalBlock__

從而可以保證無論 block 如何被傳遞,內部包裝的代碼都不會發生變化,而且執行效率高

但是實際開發中,不引用外部變量的 block 幾乎是不存在的

引用外部變量的 block

- (void)blockDemo2 {

int i =10;

void(^myBlock)() = ^ {

NSLog(@"hello world %zd", i);

? ? };

NSLog(@"%@", myBlock);

}

引用外部變量的 block 保存在:

ARC:堆區__NSMallocBlock__

MRC:棧區__NSStackBlock__

因此:在定義block 屬性時應該使用copy關鍵字,將 block 從棧區復制到堆區

定義 block 屬性

/// 定義 block 屬性

@property(nonatomic,copy)void(^demoBlock)();

記錄 block 屬性

- (void)blockDemo2 {

int i =10;

void(^myBlock)() = ^ {

NSLog(@"hello world %zd", i);

? ? };

NSLog(@"%@", myBlock);

// 錯誤的寫法,不會調用 setter 方法

// _demoBlock = myBlock;

// 正確的寫法,調用 setter 方法,并且對 block 進行 copy

self.demoBlock= myBlock;

NSLog(@"%@",self.demoBlock);

}

提示:為了避免程序員的麻煩,在 ARC 中,定義了引用外部變量的 block,默認都是在堆區的!

block的循環引用

定義一個引用self.的 block

@implementation ViewController

- (void)viewDidLoad {

? ? [superviewDidLoad];

void(^block)() = ^ {

NSLog(@"%@",self.view);

? ? };

? ? block();

}

- (void)dealloc {

NSLog(@"%s", __FUNCTION__);

}

@end

運行測試

沒有循環引用:因為 block 執行完畢后會釋放,從而釋放對self的引用

定義屬性

@property(nonatomic,copy)void(^demoBlock)();

在viewDidLoad中記錄 block

- (void)viewDidLoad {

? ? [superviewDidLoad];

void(^block)() = ^ {

NSLog(@"%@",self.view);

? ? };

// 記錄 block,等待需要的時候執行

self.demoBlock= block;

}

運行測試

循環引用發生了:

因為ViewController引用了block屬性

block內部引用了self(ViewController)

相互引用產生了循環引用

解決循環引用

使用__weak修飾符,定義一個弱引用的對象

然后讓block引用此弱引用對象,從而可以打破循環引用

__weak typeof(self) weakSelf =self;

void(^block)() = ^ {

NSLog(@"%@", weakSelf.view);

? ? };

提示:

在使用block的時候,如果出現self,同時使用屬性記錄 block 的時候,要格外注意是否會出現循環引用

注意:不是所有的self.都會出現循環引用 ——block 執行完畢就銷毀,例如 UIView 的動畫代碼

大坑:在block內部不要使用_成員變量

因為,成員變量默認是由控制器強引用的,以下代碼同樣會出現循環引用,同時非常不容易發現!

@implementation ViewController{

NSMutableArray*_arrayM;

}

- (void)viewDidLoad {

? ? [superviewDidLoad];

? ? __weak typeof(self) weakSelf =self;

void(^block)() = ^ {

NSLog(@"%@ %@", weakSelf.view, _arrayM);

? ? };

// 記錄 block,等待需要的時候執行

self.demoBlock= block;

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容

  • Block使用場景,可以在兩個界面的傳值,也可以對代碼封裝作為參數的傳遞等。用過GCD就知道Block的精妙之處。...
    Coder_JMicheal閱讀 733評論 2 1
  • iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對C語言的擴展,用來實現匿名函數的特性,B...
    smile刺客閱讀 2,365評論 2 26
  • 前言 Blocks是C語言的擴充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,781評論 0 23
  • 簡述 一句話搞懂block:可以理解為,block是對上下文代碼段的打包,然后在適當的時機執行。 block長什么...
    Allan_野草閱讀 2,177評論 0 25
  • 《Objective-C高級編程》這本書就講了三個東西:自動引用計數、block、GCD,偏向于從原理上對這些內容...
    WeiHing閱讀 9,868評論 10 69