前言
ios4.0系統(tǒng)已開始支持block,在編程過程中,block被Obj-C看成是對(duì)象,它封裝了一段代碼,這段代碼可以在任何時(shí)候執(zhí)行。block可以作為函數(shù)參數(shù)或者函數(shù)的返回值,而其本身又可以帶輸入?yún)?shù)或返回值。它和傳統(tǒng)的函數(shù)指針很類似,但是有區(qū)別:block是inline的,并且它對(duì)局部變量是只讀的。
block和函數(shù)的相似性:(1)可以保存代碼(2)有返回值(3)有形參(4)調(diào)用方式一樣。
block的使用
1、block的定義
// 聲明和實(shí)現(xiàn)寫在一起,就像變量的聲明實(shí)現(xiàn) int a = 10;
int(^aBlock)(int,int) = ^(intnum1,intnum2) {
returnnum1 * num2;
};
// 聲明和實(shí)現(xiàn)分開,就像變量先聲明后實(shí)現(xiàn) int a;a = 10;
int(^cBlock)(int,int);
cBlock = ^(intnum1,intnum2)
{
returnnum1 * num2;
};
其中,定義了一個(gè)名字為aBlock的blocks對(duì)象,并攜帶了相關(guān)信息:
1、aBlock 有兩個(gè)形式參數(shù),分別為int類型;
2、aBlock 的返回值為int 類型;
3、等式右邊就是blocks的具體實(shí)現(xiàn);
4、^ 帶邊blocks聲明和實(shí)現(xiàn)的標(biāo)示(關(guān)鍵字);
當(dāng)然,你可以定義其他形式的block。e.g:無返回值,無形式參數(shù)等;
void(^bBlock)() = ^()
{
inta =10;
printf(num = %d,a);
};
2、blocks 訪問權(quán)限
blocks可以訪問局部變量,但是不能修改。
inta =10;
int(^dBlock)(int) = ^(intnum)
{
a++;//not work!
returnnum * a;
};
此處不能修改的原因是在編譯期間確定的,編譯器編譯的時(shí)候把a(bǔ)的值復(fù)制到block作為一個(gè)新變量(假設(shè)是a‘ = 10),此時(shí)a'和a是沒有關(guān)系的。
這個(gè)地方就是函數(shù)中的值傳遞。如果要修改就要加關(guān)鍵字:__block或者static
__blockinta =7;
int(^dBlock)(int) = ^(intnum)
{
a++;// work!
returnnum * a;
};
3、block的調(diào)用
block調(diào)用就像調(diào)用函數(shù)一樣。e.g:
1intc = aBlock(10,10);
bBlock();
4、block 應(yīng)用
假設(shè)我們熟悉代理遞值的話,對(duì)代理我們可能又愛有恨!我們先建立模型A頁(yè)面 push
B頁(yè)面,如果把A頁(yè)面的值傳遞到B頁(yè)面,屬性和單例傳值可以搞定!但是如果Pop過程中把B頁(yè)面的值傳遞到A頁(yè)面,那就可以用單例或者代理了!說到代理,
我們要先聲明協(xié)議,創(chuàng)建代理,很是麻煩。常常我們傳遞一個(gè)數(shù)值需要在兩個(gè)頁(yè)面間寫很多代碼,這些代碼改變頁(yè)面的整體順序,可讀性也打了折扣。所以,此
時(shí),block是一種優(yōu)化方案!
5、 block的內(nèi)存管理
block本身是像對(duì)象一樣可以retain,和release。但是,block在創(chuàng)建的時(shí)候,它的內(nèi)存是分配在棧(stack)上,而不是在堆(heap)上。他本身的作于域是屬于創(chuàng)建時(shí)候的作用域,一旦在創(chuàng)建時(shí)候的作用域外面調(diào)用block將導(dǎo)致程序崩潰。比如下面的例子。 我在view did load中創(chuàng)建了一個(gè)block:
- (void)viewDidLoad
{
[superviewDidLoad];
int number = 1;
_block = ^(){
NSLog(@number %d, number);
};
}
并且在一個(gè)按鈕的事件中調(diào)用了這個(gè)block:
- (IBAction)testDidClick:(id)sender {
_block();
}
此時(shí)我按了按鈕之后就會(huì)導(dǎo)致程序崩潰,解決這個(gè)問題的方法就是在創(chuàng)建完block的時(shí)候需要調(diào)用copy的方法。copy會(huì)把block從棧上移動(dòng)到堆上,那么就可以在其他地方使用這個(gè)block了~ 修改代碼如下:
_block = ^(){
NSLog(@number %d, number);
};
_block = [_blockcopy];
同理,特別需要注意的地方就是在把block放到集合類當(dāng)中去的時(shí)候,如果直接把生成的block放入到集合類中,是無法在其他地方使用block,必須要對(duì)block進(jìn)行copy。不過代碼看上去相對(duì)奇怪一些:
[array addObject:[[^{
NSLog(@hello!);
} copy] autorelease]];
6、循環(huán)引用
對(duì)于非ARC下, 為了防止循環(huán)引用, 我們使用__block來修飾在Block中使用的對(duì)象:
對(duì)于ARC下, 為了防止循環(huán)引用, 我們使用__weak來修飾在Block中使用的對(duì)象。原理就是:ARC中,Block中如果引用了__strong修飾符的自動(dòng)變量,則相當(dāng)于Block對(duì)該變量的引用計(jì)數(shù)+1。
這一點(diǎn)其實(shí)是在第一點(diǎn)的一個(gè)小的衍生。當(dāng)在block內(nèi)部使用成員變量的時(shí)候,比如
@interface ViewController : UIViewController
{
NSString *_string;
}
@end
在block創(chuàng)建中:
_block = ^(){
NSLog(@string %@, _string);
};
這里的_string相當(dāng)于是self->_string;那么block是會(huì)對(duì)內(nèi)部的對(duì)象進(jìn)行一次retain。也就是說,self會(huì)被retain一次。當(dāng)self釋放的時(shí)候,需要block釋放后才會(huì)對(duì)self進(jìn)行釋放,但是block的釋放又需要等self的dealloc中才會(huì)釋放。如此一來變形成了循環(huán)引用,導(dǎo)致內(nèi)存泄露。
修改方案是新建一個(gè)__block scope的局部變量,并把self賦值給它,而在block內(nèi)部則使用這個(gè)局部變量來進(jìn)行取值。因?yàn)開_block標(biāo)記的變量是不會(huì)被自動(dòng)retain的。
__block ViewController *controller = self;
_block = ^(){
NSLog(@string %@, controller->_string);
};
博主瀏覽了很多博客,總結(jié)了一下,block實(shí)際上是(底層c++): 指向結(jié)構(gòu)體的指針,底層會(huì)創(chuàng)建一個(gè)結(jié)構(gòu)體,實(shí)現(xiàn)構(gòu)造方法,來接參數(shù),編譯器會(huì)將block的內(nèi)部代碼生成對(duì)應(yīng)的函數(shù)。
block實(shí)戰(zhàn):用block進(jìn)行頁(yè)面間傳值
使用Block的地方很多,其中傳值只是其中的一小部分,下面介紹Block在兩個(gè)界面之間的傳值:
先說一下思想:
首先,創(chuàng)建兩個(gè)視圖控制器,在第一個(gè)視圖控制器中創(chuàng)建一個(gè)UILabel和一個(gè)UIButton,其中UILabel是為了顯示第二個(gè)視圖控制器傳過來的字符串,UIButton是為了push到第二個(gè)界面。
第二個(gè)界面的只有一個(gè)UITextField,是為了輸入文字,當(dāng)輸入文字,并且返回第一個(gè)界面的時(shí)候,當(dāng)?shù)诙€(gè)視圖將要消失的時(shí)候,就將第二個(gè)界面上TextFiled中的文字傳給第一個(gè)界面,并且顯示在UILabel上。
其實(shí)核心代碼就幾行代碼:
下面是主要代碼:(因?yàn)槲沂怯胹toryBoard創(chuàng)建的工程,所以上面的屬性和相應(yīng)的方法,是使用系統(tǒng)生成的outlet)
一、在第二個(gè)視圖控制器的.h文件中定義聲明Block屬性
typedef void(^ReturnTextBlock)(NSString *showText);
@interface TextFieldViewController : UIViewController
@property(nonatomic, copy) ReturnTextBlock returnTextBlock;
- (void)returnText:(ReturnTextBlock)block;
@end
第一行代碼是為要聲明的Block重新定義了一個(gè)名字
ReturnTextBlock
這樣,下面在使用的時(shí)候就會(huì)很方便。
第三行是定義的一個(gè)Block屬性
第四行是一個(gè)在第一個(gè)界面?zhèn)鬟M(jìn)來一個(gè)Block語(yǔ)句塊的函數(shù),不用也可以,不過加上會(huì)減少代碼的書寫量
二、實(shí)現(xiàn)第二個(gè)視圖控制器的方法
- (void)returnText:(ReturnTextBlock)block {
self.returnTextBlock = block;
}
- (void)viewWillDisappear:(BOOL)animated {
if(self.returnTextBlock != nil) {
self.returnTextBlock(self.inputTF.text);
}
}
其中inputTF是視圖中的UITextField。
第一個(gè)方法就是定義的那個(gè)方法,把傳進(jìn)來的Block語(yǔ)句塊保存到本類的實(shí)例變量returnTextBlock(.h中定義的屬性)中,然后尋找一個(gè)時(shí)機(jī)調(diào)用,而這個(gè)時(shí)機(jī)就是上面說到的,當(dāng)視圖將要消失的時(shí)候,需要重寫:
- (void)viewWillDisappear:(BOOL)animated;
方法。
三、在第一個(gè)視圖中獲得第二個(gè)視圖控制器,并且用第二個(gè)視圖控制器來調(diào)用定義的屬性
如下方法中書寫:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
TextFieldViewController *tfVC = segue.destinationViewController;
[tfVC returnText:^(NSString *showText) {
self.showLabel.text = showText;
}];
}
揭開block神秘面紗
假設(shè)A有一個(gè)任務(wù),是去倉(cāng)庫(kù)取一張A4紙放到會(huì)議室,然后在紙上寫一份策劃書。取紙又要經(jīng)過倉(cāng)庫(kù)管理員,于是A通知倉(cāng)庫(kù)管理員來張紙過來。由于管理員是個(gè)老
頭動(dòng)作很慢,另外A還有其他工作,如果一直等待管理員就太浪費(fèi)時(shí)間了,合理的做法是讓倉(cāng)庫(kù)管理員進(jìn)行取紙這項(xiàng)工作,A在通知管理員后就繼續(xù)自己的工作。A
不知道倉(cāng)庫(kù)管理員什么時(shí)候能完成取紙,也就不知道什么時(shí)候可以在紙上寫策劃書。這時(shí)block機(jī)制就派上用場(chǎng)了,使用這種機(jī)制,可以讓A在通知倉(cāng)庫(kù)管理員
取紙的同時(shí),告訴倉(cāng)庫(kù)管理員將紙放到編號(hào)XX會(huì)議室,并安排好將要在紙上寫的策劃內(nèi)容,當(dāng)管理員把紙拿來后,可能過一會(huì)就會(huì)有個(gè)助理將策劃內(nèi)容寫到紙上。
我們將這個(gè)故事對(duì)應(yīng)到代碼上:
#pragma?mark?-?第三方登錄
-?(void)btnLoginWeiboClicked:(id)sender?{
[_waitCirclestartAnimating];
[[HSLoginClasscreateInstance]loginWithPlatformType:ShareTypeSinaWeibowithBlock:^(BOOLsuccess,idmessage)?{
if(success)?{
//跳出登錄頁(yè)面
[selfdismissViewControllerAnimated:YEScompletion:^{}];
[_waitCirclestopAnimating];
NSLog(@"新浪微博?%@",?message);
}else{
}
}];
//test?statistics
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{
NSDate*?date?=?[NSDatedate];
NSTimeInterval?nowTime?=?[datetimeIntervalSince1970];
NSString*?netStatus?=?(NSString*)[[NSUserDefaultsstandardUserDefaults]objectForKey:NETSTATUS];
[[HSStatisticsModulestatisticsModule]makeSession:nowTime?:OPER_IN?:@"noid"];
[[HSStatisticsModulestatisticsModule]upLoadData:netStatus?:[NSStringstringWithFormat:@"%f",nowTime]];
});
}
請(qǐng)看這段代碼,整個(gè)方法是A要做的工作,startAnimating/loginWithPlatformType/dispatch_async分別是A要做的三個(gè)任務(wù),由于loginWithPlatformType需要一段時(shí)間才能完成,并且loginWithPlatformType完成后要根據(jù)結(jié)果做相應(yīng)處理,所以我們對(duì)loginWithPlatformType進(jìn)行異步處理。block代碼段是loginWithPlatformType得到結(jié)果后要做的操作H,這里的block寫法就表示,我們先將操作H安排好,然后繼續(xù)其他工作,當(dāng)loginWithPlatformType執(zhí)行ok后自會(huì)有人(可能是那個(gè)助理)去執(zhí)行操作H。
iOS面試之block
1 什么是block
對(duì)于閉包(block),
有很多定義,其中閉包就是能夠讀取其它函數(shù)內(nèi)部變量的函數(shù),這個(gè)定義即接近本質(zhì)又較好理解。對(duì)于剛接觸Block的同學(xué),會(huì)覺得有些繞,因?yàn)槲覀兞?xí)慣寫這
樣的程序main(){ funA();} funA(){funB();} funB(){.....};
就是函數(shù)main調(diào)用函數(shù)A,函數(shù)A調(diào)用函數(shù)B...
函數(shù)們依次順序執(zhí)行,但現(xiàn)實(shí)中不全是這樣的,例如項(xiàng)目經(jīng)理M,手下有3個(gè)程序員A、B、C,當(dāng)他給程序員A安排實(shí)現(xiàn)功能F1時(shí),他并不等著A完成之后,再
去安排B去實(shí)現(xiàn)F2,而是安排給A功能F1,B功能F2,C功能F3,然后可能去寫技術(shù)文檔,而當(dāng)A遇到問題時(shí),他會(huì)來找項(xiàng)目經(jīng)理M,當(dāng)B做完時(shí),會(huì)通知
M,這就是一個(gè)異步執(zhí)行的例子。在這種情形下,Block便可大顯身手,因?yàn)樵陧?xiàng)目經(jīng)理M,給A安排工作時(shí),同時(shí)會(huì)告訴A若果遇到困難,如何能找到他報(bào)告
問題(例如打他手機(jī)號(hào)),這就是項(xiàng)目經(jīng)理M給A的一個(gè)回調(diào)接口,要回掉的操作,比如接到電話,百度查詢后,返回網(wǎng)頁(yè)內(nèi)容給A,這就是一個(gè)Block,在M
交待工作時(shí),已經(jīng)定義好,并且取得了F1的任務(wù)號(hào)(局部變量),卻是在當(dāng)A遇到問題時(shí),才調(diào)用執(zhí)行,跨函數(shù)在項(xiàng)目經(jīng)理M查詢百度,獲得結(jié)果后回調(diào)該
block。
2 block 實(shí)現(xiàn)原理
Objective-C是對(duì)C語(yǔ)言的擴(kuò)展,block的實(shí)現(xiàn)是基于指針和函數(shù)指針。
從計(jì)算語(yǔ)言的發(fā)展,最早的goto,高級(jí)語(yǔ)言的指針,到面向?qū)ο笳Z(yǔ)言的block,從機(jī)器的思維,一步步接近人的思維,以方便開發(fā)人員更為高效、直接的描述出現(xiàn)實(shí)的邏輯(需求)。
下面是兩篇很好的介紹block實(shí)現(xiàn)的博文
談Objective-C Block的實(shí)現(xiàn)
3 block的使用
使用實(shí)例
cocoaTouch框架下動(dòng)畫效果的Block的調(diào)用
使用typed聲明block
typedef void(^didFinishBlock) (NSObject *ob);
這就聲明了一個(gè)didFinishBlock類型的block,
然后便可用
@property (nonatomic,copy) didFinishBlock? finishBlock;
聲明一個(gè)block對(duì)象,注意對(duì)象屬性設(shè)置為copy,接到block 參數(shù)時(shí),便會(huì)自動(dòng)復(fù)制一份。
__block是一種特殊類型,
使用該關(guān)鍵字聲明的局部變量,可以被block所改變,并且其在原函數(shù)中的值會(huì)被改變。
4 常見系列面試題
面試時(shí),面試官會(huì)先問一些,是否了解block,是否使用過block,這些問題相當(dāng)于開場(chǎng)白,往往是下面一系列問題的開始,所以一定要如實(shí)根據(jù)自己的情況回答。
1 使用block和使用delegate完成委托模式有什么優(yōu)點(diǎn)?
首先要了解什么是委托模式,委托模式在iOS中大量應(yīng)用,其在設(shè)計(jì)模式中是適配器模式中的對(duì)象適配器,Objective-C中使用id類型指向一切對(duì)象,使委托模式在iOS中的實(shí)現(xiàn)更為方便。了解委托模式的細(xì)節(jié):
使用block實(shí)現(xiàn)委托模式,其優(yōu)點(diǎn)是回調(diào)的block代碼塊定義在委托對(duì)象函數(shù)內(nèi)部,使代碼更為緊湊;
適配對(duì)象不再需要實(shí)現(xiàn)具體某個(gè)protocol,代碼更為簡(jiǎn)潔。
2 多線程與block
GCD與Block
使用 dispatch_async系列方法,可以以指定的方式執(zhí)行block
dispatch_async的完整定義
void dispatch_async(
dispatch_queue_t queue,
dispatch_block_t block);
功能:在指定的隊(duì)列里提交一個(gè)異步執(zhí)行的block,不阻塞當(dāng)前線程
通過queue來控制block執(zhí)行的線程。主線程執(zhí)行前文定義的 finishBlock對(duì)象
dispatch_async(dispatch_get_main_queue(),^(void){finishBlock();});
參考博客:
http://blog.csdn.net/xunyn/article/details/11658261
http://www.2cto.com/kf/201504/388349.html
博主的微博、CocoaChina博客、CSDN博客同步更新,歡迎關(guān)注:
新浪微博:http://weibo.com/p/1005052308506177/home?from=page_100505_profile&wvr=6&mod=data&is_all=1#place
CocoaChina:http://blog.cocoachina.com/477998
CSDN:http://blog.csdn.net/czkyes