
注:本文為筆記形式,所以很多都是摘抄的.<<iOS 與OS X多線程和內(nèi)存管理>>書中寫的很棒,簡單易懂,建議各位看官自己去看看.
前言
前面兩篇,我們主要說到的是關(guān)于與內(nèi)存管理相關(guān)的技術(shù),接下來,我們深入了解關(guān)于隱形函數(shù)Blocks的相關(guān)技術(shù).其實在華山論劍之淺談iOS的 target - action設(shè)計模式 和 代理模式 以及Bolck中也說過Block相關(guān)的使用方式,不過都略顯粗糙,所以這次結(jié)合著<<iOS 與OS X多線程和內(nèi)存管理>>這本書,來深入了解一下關(guān)于Blocks的相關(guān)技術(shù).

Blocks簡介
在書中說到int,float是C語言的擴充功能,它是一個帶有自動變量的匿名函數(shù).在日常開發(fā)過程中,我覺得與其說Blocks是一個匿名函數(shù),不如說是它是一種數(shù)據(jù)類型更好理解一點,像int,float一樣存儲數(shù)據(jù),不過int,float存儲的是變量,而Blocks存儲的卻是一個函數(shù).
比如我們可以把一個Blocks聲明成一個類的屬性.首先,在ViewController中,我們先給Block變量改一下名稱,我們就可以聲明成ViewController的屬性了.如下所示.
typedef int(^Block)(void);
@interface ViewController : UIViewController
@property(nonatomic,copy)Block block;
@end
然后我們就可以給屬性賦值了.比如我們?nèi)缦逻M行一個簡單的賦值.
self.block = ^int{ return 1; };
調(diào)用也是相當?shù)暮唵?如下所示.
self.block();
那么在實際開發(fā)過程中,Blocks主要是用于什么樣的場景呢?或者說Blocks的主要作用是什么呢?在我們實際開發(fā)過程中,Blocks主要用于傳值和回調(diào),這一點類似于OC中代理協(xié)議模式.現(xiàn)在我們舉個例子說明一下它的主要作用.
假設(shè)有兩個控制器,一個是ViewController對象,另外一個是NewViewController對象;其中,NewViewController對象是通過ViewController對象pushViewController推出來的,我們的目的是通過在NewViewController進行操作,然后給ViewController進行傳值操作.示意圖如下所示.

接下來我們一步一步的進行實現(xiàn),首先,我們需要在NewViewController聲明Block屬性,Block的類型是有參數(shù),無返回值類型的.如下所示.
#import <UIKit/UIKit.h>
typedef void(^Block)(int);
@interface NewViewController : UIViewController
@property(nonatomic,copy)Block block;
@end
接下來,我們在ViewController中創(chuàng)建NewViewController對象,然后對Block進行賦值.
NewViewController *newVC = [[NewViewController alloc]init];
newVC.block = ^(int number){
printf("value = %d",number);
};
緊接著就是通過pushViewController這方法推出NewViewController控制器頁面.
[self.navigationController pushViewController:newVC animated:YES];
然后,我們在NewViewController控制器中進行Block的調(diào)用.
self.block(666);
通過我們調(diào)用,我們就可以在控制臺打印出如下的信息了.

具體可對照查看BlockDemo
Blocks語法
對于Blocks語法,騷棟只想用一句話來表述,那就是能省則省,我們首先看一個最完整的Blocks的寫法,如下圖所示.

我們在這舉一個例子來說明一下.
^int(int number){
return number;
}
接下來,我們則說一下什么叫能省則省.凡是沒有的部分我們都可以省去,比如我們沒有返回值,我們表示的完整方式如下所示.
^ void (int number){
printf(@"value = %d",number);
}
上面的這個block等同于下面的block表達式.
^ (int number){
printf(@"value = %d",number);
}
再比如,如果我們參數(shù)也沒有,返回值也沒有的block,用完整的形式如下表示可以如下表示.
^ void ( void ){
printf(@"value = %d",number);
}
等同于
^ {
printf(@"value = %d",number);
}
</br>
Blocks類型變量
在Blocks簡介中,我們就對Blocks變量作為對象屬性進行了簡單的舉例.其實,我們在實際開發(fā)過程中也是這樣用的,我們一般要先給block改名,這是為什么呢,因為block很長,所以我們給block改名字后減少了我們代碼量,而且形式上更加容易理解.為了比對改名前后的變化,我們舉例來說明一下.
首先我們定義一個沒有改名的block,然后我們對其進行賦值.
int (^blk)(void);
blk= ^(void){
return 1;
};
然后,我們先給block進行改名操作之后,再進行進行賦值操作.
typedef int(^Block)(void);
然后聲明變量,緊接著進行賦值.
Block block;
block = ^(void){
return 1;
};
截獲自動變量值和截獲自動變量
block具有截獲自動變量值的功能,那么什么叫截獲自動變量值呢?我們看下面的示例.
int i = 10;
void (^blk)(void) = ^{
printf("Value = %d",i);
};
i = 5;
blk();
printf("value = %d",i);
我們看上面的代碼,首先i的初始值為10,然后我們創(chuàng)建了一個隱形函數(shù)block,并在其中打印了i的值,但是這時候,我們并沒有調(diào)用這個隱性函數(shù),當我們把i重新賦值為5之后,緊接著我們調(diào)用上面的隱性函數(shù).那么我們控制器將會打印出處如下所示的結(jié)果.

從而,我們知道,當隱形函數(shù)block進行聲明實現(xiàn)的時候就截獲了i的值為10,所以調(diào)用的時候并不是5,而是10.這就是隱形函數(shù)block的截獲自動變量值.
那么,截獲自動變量又是什么意思呢?主要是指截獲OC中的對象,比如我們截獲一個數(shù)組并給他添加元素.
NSMutableArray *array = [[NSMutableArray alloc]init];
void (^blk)(void) = ^{
id obj = [[NSObject alloc]init];
[array addObject:obj];
};
問題來了,我們可不可以直接通過block給變量賦值呢?如下所示.
int i = 10;
void (^blk)(void) = ^{i = 1;};
或者是這樣對對象進行重新賦值操作.
NSMutableArray *array ;
void (^blk)(void) = ^{
array = [[NSMutableArray alloc]init];
};
那么如果你這樣寫了,直接會報編譯錯誤.如下圖所示.


那么這又是怎么回事呢?我們上面不是明明能打印嗎?為什么這樣使用block就會出現(xiàn)問題呢?我們又該如何解決呢?我們接著看下一個模塊.
</br>
__block說明符
上面的問題是怎么出現(xiàn)的呢?在書中是這樣說的,如果用C語言來描述,即是截獲NSMutableArray類的對象用的結(jié)構(gòu)體實例指針,雖然賦值給截獲的自動變量array的操作會產(chǎn)生編譯錯誤,但使用截獲的值卻不會有任何的問題.我們可以總結(jié)為以下的幾句話.
不管是基本數(shù)據(jù)類型的變量還是對象變量.在使用block對其進行操作的時候,我們允許使用變量的值,但是不允許直接給變量進行賦值操作.
那么我們該如何解決這個問題呢?我們只需要在block需要賦值的變量 前面加上一個__block說明符即可.如下所示.
__block int i = 10;
void (^blk)(void) = ^{i = 1;};
__block NSMutableArray *array ;
void (^blk)(void) = ^{
array = [[NSMutableArray alloc]init];
};
這樣,就不會再報編譯錯誤了.我們也就完美解決了上述的問題了.
</br>
Blocks循環(huán)引用
Blocks循環(huán)引用是什么原因造成的呢?Blocks循環(huán)引用跟我們前一篇博客中說的自己引用自己的情況差不多.如下圖所示.

具體的代碼示例,我們可以在一個控制器進行演示,整體代碼如下所示.
#import "NewViewController.h"
typedef void(^Block)(void);
@interface NewViewController ()
@property(nonatomic,copy)Block block;
@property(nonatomic,strong)NSString *objString;
@end
@implementation NewViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.objString = @"你好";
self.block = ^(){
NSLog(@"%@",self.objString);
};
}
@end
我們首先進行在控制器的延展部分聲明了block屬性對象和一個測試用的字符串屬性對象.
@property(nonatomic,copy)Block block;
@property(nonatomic,strong)NSString *objString;
然后我們在ViewDidLoad方面方法對字符串進行賦值.
self.objString = @"你好";
緊接著,我們就在block的實現(xiàn)中使用字符串.
self.block = ^(){
NSLog(@"%@",self.objString);
};
這時候Xocde會直接拋出一個編譯警告,意思就是存在內(nèi)存泄漏.

那么解決方案也是跟前一篇博客說的那樣,直接使用__weak修飾符.如下所示.
__weak typeof(self)tmpe =self;
self.block = ^(){
NSLog(@"%@",tmpe.objString);
};
尾聲
第三個模塊是關(guān)于多線程GCD的API相關(guān)因為以前都做過了,所以接下來,將看另外一本書,這本<<iOS 與OS X多線程和內(nèi)存管理>>就到這了.歡迎大家一起來討論<<iOS 與OS X多線程和內(nèi)存管理>>相關(guān)問題,如果有任何問題,歡迎聯(lián)系騷棟,謝謝.最后還是<<iOS 與OS X多線程和內(nèi)存管理>>的pdf版的下載傳送門,各位看官可以自行去參考查看.
<<iOS 與OS X多線程和內(nèi)存管理>>的pdf版?zhèn)魉烷T??
