注:本文為筆記形式,所以很多都是摘抄的.<<iOS 與OS X多線程和內(nèi)存管理>>書中寫的很棒,簡(jiǎn)單易懂,建議各位看官自己去看看.
前言
前面兩篇,我們主要說(shuō)到的是關(guān)于與內(nèi)存管理相關(guān)的技術(shù),接下來(lái),我們深入了解關(guān)于隱形函數(shù)Blocks的相關(guān)技術(shù).其實(shí)在華山論劍之淺談iOS的 target - action設(shè)計(jì)模式 和 代理模式 以及Bolck中也說(shuō)過(guò)Block相關(guān)的使用方式,不過(guò)都略顯粗糙,所以這次結(jié)合著<<iOS 與OS X多線程和內(nèi)存管理>>這本書,來(lái)深入了解一下關(guān)于Blocks的相關(guān)技術(shù).
Blocks簡(jiǎn)介
在書中說(shuō)到int,float是C語(yǔ)言的擴(kuò)充功能,它是一個(gè)帶有自動(dòng)變量的匿名函數(shù).在日常開(kāi)發(fā)過(guò)程中,我覺(jué)得與其說(shuō)Blocks是一個(gè)匿名函數(shù),不如說(shuō)是它是一種數(shù)據(jù)類型更好理解一點(diǎn),像int,float一樣存儲(chǔ)數(shù)據(jù),不過(guò)int,float存儲(chǔ)的是變量,而B(niǎo)locks存儲(chǔ)的卻是一個(gè)函數(shù).
比如我們可以把一個(gè)Blocks聲明成一個(gè)類的屬性.首先,在ViewController中,我們先給Block變量改一下名稱,我們就可以聲明成ViewController的屬性了.如下所示.
typedef int(^Block)(void);
@interface ViewController : UIViewController
@property(nonatomic,copy)Block block;
@end
然后我們就可以給屬性賦值了.比如我們?nèi)缦逻M(jìn)行一個(gè)簡(jiǎn)單的賦值.
self.block = ^int{ return 1; };
調(diào)用也是相當(dāng)?shù)暮?jiǎn)單,如下所示.
self.block();
那么在實(shí)際開(kāi)發(fā)過(guò)程中,Blocks主要是用于什么樣的場(chǎng)景呢?或者說(shuō)Blocks的主要作用是什么呢?在我們實(shí)際開(kāi)發(fā)過(guò)程中,Blocks主要用于傳值和回調(diào),這一點(diǎn)類似于OC中代理協(xié)議模式.現(xiàn)在我們舉個(gè)例子說(shuō)明一下它的主要作用.
假設(shè)有兩個(gè)控制器,一個(gè)是ViewController對(duì)象,另外一個(gè)是NewViewController對(duì)象;其中,NewViewController對(duì)象是通過(guò)ViewController對(duì)象pushViewController
推出來(lái)的,我們的目的是通過(guò)在NewViewController進(jìn)行操作,然后給ViewController進(jìn)行傳值操作.示意圖如下所示.
接下來(lái)我們一步一步的進(jìn)行實(shí)現(xiàn),首先,我們需要在NewViewController聲明Block屬性,Block的類型是有參數(shù),無(wú)返回值類型的.如下所示.
#import <UIKit/UIKit.h>
typedef void(^Block)(int);
@interface NewViewController : UIViewController
@property(nonatomic,copy)Block block;
@end
接下來(lái),我們?cè)赩iewController中創(chuàng)建NewViewController對(duì)象,然后對(duì)Block進(jìn)行賦值.
NewViewController *newVC = [[NewViewController alloc]init];
newVC.block = ^(int number){
printf("value = %d",number);
};
緊接著就是通過(guò)pushViewController
這方法推出NewViewController控制器頁(yè)面.
[self.navigationController pushViewController:newVC animated:YES];
然后,我們?cè)贜ewViewController控制器中進(jìn)行Block的調(diào)用.
self.block(666);
通過(guò)我們調(diào)用,我們就可以在控制臺(tái)打印出如下的信息了.
具體可對(duì)照查看BlockDemo
Blocks語(yǔ)法
對(duì)于Blocks語(yǔ)法,騷棟只想用一句話來(lái)表述,那就是能省則省,我們首先看一個(gè)最完整的Blocks的寫法,如下圖所示.
我們?cè)谶@舉一個(gè)例子來(lái)說(shuō)明一下.
^int(int number){
return number;
}
接下來(lái),我們則說(shuō)一下什么叫能省則省.凡是沒(méi)有的部分我們都可以省去,比如我們沒(méi)有返回值,我們表示的完整方式如下所示.
^ void (int number){
printf(@"value = %d",number);
}
上面的這個(gè)block等同于下面的block表達(dá)式.
^ (int number){
printf(@"value = %d",number);
}
再比如,如果我們參數(shù)也沒(méi)有,返回值也沒(méi)有的block,用完整的形式如下表示可以如下表示.
^ void ( void ){
printf(@"value = %d",number);
}
等同于
^ {
printf(@"value = %d",number);
}
</br>
Blocks類型變量
在Blocks簡(jiǎn)介中,我們就對(duì)Blocks變量作為對(duì)象屬性進(jìn)行了簡(jiǎn)單的舉例.其實(shí),我們?cè)趯?shí)際開(kāi)發(fā)過(guò)程中也是這樣用的,我們一般要先給block改名,這是為什么呢,因?yàn)閎lock很長(zhǎng),所以我們給block改名字后減少了我們代碼量,而且形式上更加容易理解.為了比對(duì)改名前后的變化,我們舉例來(lái)說(shuō)明一下.
首先我們定義一個(gè)沒(méi)有改名的block,然后我們對(duì)其進(jìn)行賦值.
int (^blk)(void);
blk= ^(void){
return 1;
};
然后,我們先給block進(jìn)行改名操作之后,再進(jìn)行進(jìn)行賦值操作.
typedef int(^Block)(void);
然后聲明變量,緊接著進(jìn)行賦值.
Block block;
block = ^(void){
return 1;
};
截獲自動(dòng)變量值和截獲自動(dòng)變量
block具有截獲自動(dòng)變量值的功能,那么什么叫截獲自動(dòng)變量值呢?我們看下面的示例.
int i = 10;
void (^blk)(void) = ^{
printf("Value = %d",i);
};
i = 5;
blk();
printf("value = %d",i);
我們看上面的代碼,首先i的初始值為10,然后我們創(chuàng)建了一個(gè)隱形函數(shù)block,并在其中打印了i的值,但是這時(shí)候,我們并沒(méi)有調(diào)用這個(gè)隱性函數(shù),當(dāng)我們把i重新賦值為5之后,緊接著我們調(diào)用上面的隱性函數(shù).那么我們控制器將會(huì)打印出處如下所示的結(jié)果.
從而,我們知道,當(dāng)隱形函數(shù)block進(jìn)行聲明實(shí)現(xiàn)的時(shí)候就截獲了i的值為10,所以調(diào)用的時(shí)候并不是5,而是10.這就是隱形函數(shù)block的截獲自動(dòng)變量值.
那么,截獲自動(dòng)變量又是什么意思呢?主要是指截獲OC中的對(duì)象,比如我們截獲一個(gè)數(shù)組并給他添加元素.
NSMutableArray *array = [[NSMutableArray alloc]init];
void (^blk)(void) = ^{
id obj = [[NSObject alloc]init];
[array addObject:obj];
};
問(wèn)題來(lái)了,我們可不可以直接通過(guò)block給變量賦值呢?如下所示.
int i = 10;
void (^blk)(void) = ^{i = 1;};
或者是這樣對(duì)對(duì)象進(jìn)行重新賦值操作.
NSMutableArray *array ;
void (^blk)(void) = ^{
array = [[NSMutableArray alloc]init];
};
那么如果你這樣寫了,直接會(huì)報(bào)編譯錯(cuò)誤.如下圖所示.
那么這又是怎么回事呢?我們上面不是明明能打印嗎?為什么這樣使用block就會(huì)出現(xiàn)問(wèn)題呢?我們又該如何解決呢?我們接著看下一個(gè)模塊.
</br>
__block說(shuō)明符
上面的問(wèn)題是怎么出現(xiàn)的呢?在書中是這樣說(shuō)的,如果用C語(yǔ)言來(lái)描述,即是截獲NSMutableArray類的對(duì)象用的結(jié)構(gòu)體實(shí)例指針,雖然賦值給截獲的自動(dòng)變量array的操作會(huì)產(chǎn)生編譯錯(cuò)誤,但使用截獲的值卻不會(huì)有任何的問(wèn)題.我們可以總結(jié)為以下的幾句話.
不管是基本數(shù)據(jù)類型的變量還是對(duì)象變量.在使用block對(duì)其進(jìn)行操作的時(shí)候,我們?cè)试S使用變量的值,但是不允許直接給變量進(jìn)行賦值操作.
那么我們?cè)撊绾谓鉀Q這個(gè)問(wèn)題呢?我們只需要在block需要賦值的變量 前面加上一個(gè)__block
說(shuō)明符即可.如下所示.
__block int i = 10;
void (^blk)(void) = ^{i = 1;};
__block NSMutableArray *array ;
void (^blk)(void) = ^{
array = [[NSMutableArray alloc]init];
};
這樣,就不會(huì)再報(bào)編譯錯(cuò)誤了.我們也就完美解決了上述的問(wèn)題了.
</br>
Blocks循環(huán)引用
Blocks循環(huán)引用是什么原因造成的呢?Blocks循環(huán)引用跟我們前一篇博客中說(shuō)的自己引用自己的情況差不多.如下圖所示.
具體的代碼示例,我們可以在一個(gè)控制器進(jì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
我們首先進(jìn)行在控制器的延展部分聲明了block屬性對(duì)象和一個(gè)測(cè)試用的字符串屬性對(duì)象.
@property(nonatomic,copy)Block block;
@property(nonatomic,strong)NSString *objString;
然后我們?cè)赩iewDidLoad方面方法對(duì)字符串進(jìn)行賦值.
self.objString = @"你好";
緊接著,我們就在block的實(shí)現(xiàn)中使用字符串.
self.block = ^(){
NSLog(@"%@",self.objString);
};
這時(shí)候Xocde會(huì)直接拋出一個(gè)編譯警告,意思就是存在內(nèi)存泄漏.
那么解決方案也是跟前一篇博客說(shuō)的那樣,直接使用__weak修飾符.如下所示.
__weak typeof(self)tmpe =self;
self.block = ^(){
NSLog(@"%@",tmpe.objString);
};
尾聲
第三個(gè)模塊是關(guān)于多線程GCD的API相關(guān)因?yàn)橐郧岸甲鲞^(guò)了,所以接下來(lái),將看另外一本書,這本<<iOS 與OS X多線程和內(nèi)存管理>>就到這了.歡迎大家一起來(lái)討論<<iOS 與OS X多線程和內(nèi)存管理>>相關(guān)問(wèn)題,如果有任何問(wèn)題,歡迎聯(lián)系騷棟,謝謝.最后還是<<iOS 與OS X多線程和內(nèi)存管理>>的pdf版的下載傳送門,各位看官可以自行去參考查看.