前言
ios4.0系統(tǒng)已開始支持block属韧,在編程過程中述吸,block被Obj-C看成是對象,它封裝了一段代碼啥么,這段代碼可以在任何時候執(zhí)行。block可以作為函數(shù)參數(shù)或者函數(shù)的返回值贰逾,而其本身又可以帶輸入?yún)?shù)或返回值悬荣。它和傳統(tǒng)的函數(shù)指針很類似,但是有區(qū)別:block是inline的疙剑,并且它對局部變量是只讀的氯迂。
block和函數(shù)的相似性:(1)可以保存代碼(2)有返回值(3)有形參(4)調(diào)用方式一樣践叠。
block的使用
1、block的定義
// 聲明和實現(xiàn)寫在一起嚼蚀,就像變量的聲明實現(xiàn) int a = 10;
int(^aBlock)(int,int) = ^(intnum1,intnum2) {
returnnum1 * num2;
};
// 聲明和實現(xiàn)分開禁灼,就像變量先聲明后實現(xiàn) int a;a = 10;
int(^cBlock)(int,int);
cBlock = ^(intnum1,intnum2)
{
returnnum1 * num2;
};
其中,定義了一個名字為aBlock的blocks對象轿曙,并攜帶了相關(guān)信息:
1弄捕、aBlock 有兩個形式參數(shù),分別為int類型导帝;
2守谓、aBlock 的返回值為int 類型;
3您单、等式右邊就是blocks的具體實現(xiàn)分飞;
4、^ 帶邊blocks聲明和實現(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;
};
此處不能修改的原因是在編譯期間確定的痒钝,編譯器編譯的時候把a的值復(fù)制到block作為一個新變量(假設(shè)是a‘ = 10)秉颗,此時a'和a是沒有關(guān)系的。
這個地方就是函數(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è)我們熟悉代理遞值的話,對代理我們可能又愛有恨晌块!我們先建立模型A頁面 push
B頁面爱沟,如果把A頁面的值傳遞到B頁面,屬性和單例傳值可以搞定匆背!但是如果Pop過程中把B頁面的值傳遞到A頁面呼伸,那就可以用單例或者代理了!說到代理钝尸,
我們要先聲明協(xié)議括享,創(chuàng)建代理闽铐,很是麻煩。常常我們傳遞一個數(shù)值需要在兩個頁面間寫很多代碼奶浦,這些代碼改變頁面的整體順序兄墅,可讀性也打了折扣。所以澳叉,此
時隙咸,block是一種優(yōu)化方案!
5成洗、 block的內(nèi)存管理
block本身是像對象一樣可以retain五督,和release。但是瓶殃,block在創(chuàng)建的時候充包,它的內(nèi)存是分配在棧(stack)上,而不是在堆(heap)上遥椿。他本身的作于域是屬于創(chuàng)建時候的作用域基矮,一旦在創(chuàng)建時候的作用域外面調(diào)用block將導(dǎo)致程序崩潰。比如下面的例子冠场。 我在view did load中創(chuàng)建了一個block:
- (void)viewDidLoad
{
[superviewDidLoad];
int number = 1;
_block = ^(){
NSLog(@number %d, number);
};
}
并且在一個按鈕的事件中調(diào)用了這個block:
- (IBAction)testDidClick:(id)sender {
_block();
}
此時我按了按鈕之后就會導(dǎo)致程序崩潰家浇,解決這個問題的方法就是在創(chuàng)建完block的時候需要調(diào)用copy的方法。copy會把block從棧上移動到堆上碴裙,那么就可以在其他地方使用這個block了~ 修改代碼如下:
_block = ^(){
NSLog(@number %d, number);
};
_block = [_blockcopy];
同理钢悲,特別需要注意的地方就是在把block放到集合類當(dāng)中去的時候,如果直接把生成的block放入到集合類中舔株,是無法在其他地方使用block莺琳,必須要對block進(jìn)行copy。不過代碼看上去相對奇怪一些:
[array addObject:[[^{
NSLog(@hello!);
} copy] autorelease]];
6载慈、循環(huán)引用
對于非ARC下, 為了防止循環(huán)引用, 我們使用__block來修飾在Block中使用的對象:
對于ARC下, 為了防止循環(huán)引用, 我們使用__weak來修飾在Block中使用的對象惭等。原理就是:ARC中,Block中如果引用了__strong修飾符的自動變量娃肿,則相當(dāng)于Block對該變量的引用計數(shù)+1咕缎。
這一點其實是在第一點的一個小的衍生珠十。當(dāng)在block內(nèi)部使用成員變量的時候料扰,比如
@interface ViewController : UIViewController
{
NSString *_string;
}
@end
在block創(chuàng)建中:
_block = ^(){
NSLog(@string %@, _string);
};
這里的_string相當(dāng)于是self->_string;那么block是會對內(nèi)部的對象進(jìn)行一次retain焙蹭。也就是說晒杈,self會被retain一次。當(dāng)self釋放的時候孔厉,需要block釋放后才會對self進(jìn)行釋放拯钻,但是block的釋放又需要等self的dealloc中才會釋放帖努。如此一來變形成了循環(huán)引用,導(dǎo)致內(nèi)存泄露粪般。
修改方案是新建一個__block scope的局部變量拼余,并把self賦值給它,而在block內(nèi)部則使用這個局部變量來進(jìn)行取值亩歹。因為__block標(biāo)記的變量是不會被自動retain的匙监。
__block ViewController *controller = self;
_block = ^(){
NSLog(@string %@, controller->_string);
};
博主瀏覽了很多博客,總結(jié)了一下小作,block實際上是(底層c++): 指向結(jié)構(gòu)體的指針亭姥,底層會創(chuàng)建一個結(jié)構(gòu)體,實現(xiàn)構(gòu)造方法顾稀,來接參數(shù)达罗,編譯器會將block的內(nèi)部代碼生成對應(yīng)的函數(shù)。
block實戰(zhàn):用block進(jìn)行頁面間傳值
使用Block的地方很多静秆,其中傳值只是其中的一小部分粮揉,下面介紹Block在兩個界面之間的傳值:
先說一下思想:
首先,創(chuàng)建兩個視圖控制器抚笔,在第一個視圖控制器中創(chuàng)建一個UILabel和一個UIButton滔蝉,其中UILabel是為了顯示第二個視圖控制器傳過來的字符串,UIButton是為了push到第二個界面塔沃。
第二個界面的只有一個UITextField蝠引,是為了輸入文字,當(dāng)輸入文字蛀柴,并且返回第一個界面的時候螃概,當(dāng)?shù)诙€視圖將要消失的時候,就將第二個界面上TextFiled中的文字傳給第一個界面鸽疾,并且顯示在UILabel上吊洼。
其實核心代碼就幾行代碼:
下面是主要代碼:(因為我是用storyBoard創(chuàng)建的工程,所以上面的屬性和相應(yīng)的方法制肮,是使用系統(tǒng)生成的outlet)
一冒窍、在第二個視圖控制器的.h文件中定義聲明Block屬性
typedef void(^ReturnTextBlock)(NSString *showText);
@interface TextFieldViewController : UIViewController
@property(nonatomic, copy) ReturnTextBlock returnTextBlock;
- (void)returnText:(ReturnTextBlock)block;
@end
第一行代碼是為要聲明的Block重新定義了一個名字
ReturnTextBlock
這樣,下面在使用的時候就會很方便豺鼻。
第三行是定義的一個Block屬性
第四行是一個在第一個界面?zhèn)鬟M(jìn)來一個Block語句塊的函數(shù)综液,不用也可以,不過加上會減少代碼的書寫量
二儒飒、實現(xiàn)第二個視圖控制器的方法
- (void)returnText:(ReturnTextBlock)block {
self.returnTextBlock = block;
}
- (void)viewWillDisappear:(BOOL)animated {
if(self.returnTextBlock != nil) {
self.returnTextBlock(self.inputTF.text);
}
}
其中inputTF是視圖中的UITextField谬莹。
第一個方法就是定義的那個方法,把傳進(jìn)來的Block語句塊保存到本類的實例變量returnTextBlock(.h中定義的屬性)中,然后尋找一個時機調(diào)用附帽,而這個時機就是上面說到的埠戳,當(dāng)視圖將要消失的時候,需要重寫:
- (void)viewWillDisappear:(BOOL)animated;
方法蕉扮。
三整胃、在第一個視圖中獲得第二個視圖控制器,并且用第二個視圖控制器來調(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有一個任務(wù)喳钟,是去倉庫取一張A4紙放到會議室爪模,然后在紙上寫一份策劃書舌菜。取紙又要經(jīng)過倉庫管理員吩屹,于是A通知倉庫管理員來張紙過來。由于管理員是個老
頭動作很慢陕壹,另外A還有其他工作应狱,如果一直等待管理員就太浪費時間了共郭,合理的做法是讓倉庫管理員進(jìn)行取紙這項工作,A在通知管理員后就繼續(xù)自己的工作疾呻。A
不知道倉庫管理員什么時候能完成取紙除嘹,也就不知道什么時候可以在紙上寫策劃書。這時block機制就派上用場了岸蜗,使用這種機制尉咕,可以讓A在通知倉庫管理員
取紙的同時,告訴倉庫管理員將紙放到編號XX會議室璃岳,并安排好將要在紙上寫的策劃內(nèi)容年缎,當(dāng)管理員把紙拿來后,可能過一會就會有個助理將策劃內(nèi)容寫到紙上铃慷。
我們將這個故事對應(yīng)到代碼上:
#pragma?mark?-?第三方登錄
-?(void)btnLoginWeiboClicked:(id)sender?{
[_waitCirclestartAnimating];
[[HSLoginClasscreateInstance]loginWithPlatformType:ShareTypeSinaWeibowithBlock:^(BOOLsuccess,idmessage)?{
if(success)?{
//跳出登錄頁面
[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]];
});
}
請看這段代碼单芜,整個方法是A要做的工作,startAnimating/loginWithPlatformType/dispatch_async分別是A要做的三個任務(wù)犁柜,由于loginWithPlatformType需要一段時間才能完成洲鸠,并且loginWithPlatformType完成后要根據(jù)結(jié)果做相應(yīng)處理,所以我們對loginWithPlatformType進(jìn)行異步處理馋缅。block代碼段是loginWithPlatformType得到結(jié)果后要做的操作H扒腕,這里的block寫法就表示,我們先將操作H安排好萤悴,然后繼續(xù)其他工作瘾腰,當(dāng)loginWithPlatformType執(zhí)行ok后自會有人(可能是那個助理)去執(zhí)行操作H。
iOS面試之block
1 什么是block
對于閉包(block),
有很多定義稚疹,其中閉包就是能夠讀取其它函數(shù)內(nèi)部變量的函數(shù)居灯,這個定義即接近本質(zhì)又較好理解祭务。對于剛接觸Block的同學(xué)内狗,會覺得有些繞怪嫌,因為我們習(xí)慣寫這
樣的程序main(){ funA();} funA(){funB();} funB(){.....};
就是函數(shù)main調(diào)用函數(shù)A,函數(shù)A調(diào)用函數(shù)B...
函數(shù)們依次順序執(zhí)行柳沙,但現(xiàn)實中不全是這樣的岩灭,例如項目經(jīng)理M,手下有3個程序員A赂鲤、B噪径、C,當(dāng)他給程序員A安排實現(xiàn)功能F1時数初,他并不等著A完成之后找爱,再
去安排B去實現(xiàn)F2,而是安排給A功能F1泡孩,B功能F2车摄,C功能F3,然后可能去寫技術(shù)文檔仑鸥,而當(dāng)A遇到問題時吮播,他會來找項目經(jīng)理M,當(dāng)B做完時眼俊,會通知
M意狠,這就是一個異步執(zhí)行的例子。在這種情形下疮胖,Block便可大顯身手环戈,因為在項目經(jīng)理M,給A安排工作時澎灸,同時會告訴A若果遇到困難谷市,如何能找到他報告
問題(例如打他手機號),這就是項目經(jīng)理M給A的一個回調(diào)接口击孩,要回掉的操作迫悠,比如接到電話,百度查詢后巩梢,返回網(wǎng)頁內(nèi)容給A创泄,這就是一個Block,在M
交待工作時括蝠,已經(jīng)定義好鞠抑,并且取得了F1的任務(wù)號(局部變量),卻是在當(dāng)A遇到問題時忌警,才調(diào)用執(zhí)行搁拙,跨函數(shù)在項目經(jīng)理M查詢百度秒梳,獲得結(jié)果后回調(diào)該
block。
2 block 實現(xiàn)原理
Objective-C是對C語言的擴展箕速,block的實現(xiàn)是基于指針和函數(shù)指針酪碘。
從計算語言的發(fā)展,最早的goto盐茎,高級語言的指針兴垦,到面向?qū)ο笳Z言的block,從機器的思維字柠,一步步接近人的思維探越,以方便開發(fā)人員更為高效、直接的描述出現(xiàn)實的邏輯(需求)窑业。
下面是兩篇很好的介紹block實現(xiàn)的博文
3 block的使用
使用實例
cocoaTouch框架下動畫效果的Block的調(diào)用
使用typed聲明block
typedef void(^didFinishBlock) (NSObject *ob);
這就聲明了一個didFinishBlock類型的block钦幔,
然后便可用
@property (nonatomic,copy) didFinishBlock? finishBlock;
聲明一個block對象,注意對象屬性設(shè)置為copy常柄,接到block 參數(shù)時鲤氢,便會自動復(fù)制一份。
__block是一種特殊類型拐纱,
使用該關(guān)鍵字聲明的局部變量铜异,可以被block所改變,并且其在原函數(shù)中的值會被改變秸架。
4 常見系列面試題
面試時揍庄,面試官會先問一些,是否了解block东抹,是否使用過block蚂子,這些問題相當(dāng)于開場白,往往是下面一系列問題的開始缭黔,所以一定要如實根據(jù)自己的情況回答食茎。
1 使用block和使用delegate完成委托模式有什么優(yōu)點?
首先要了解什么是委托模式馏谨,委托模式在iOS中大量應(yīng)用别渔,其在設(shè)計模式中是適配器模式中的對象適配器,Objective-C中使用id類型指向一切對象惧互,使委托模式在iOS中的實現(xiàn)更為方便哎媚。了解委托模式的細(xì)節(jié):
使用block實現(xiàn)委托模式,其優(yōu)點是回調(diào)的block代碼塊定義在委托對象函數(shù)內(nèi)部喊儡,使代碼更為緊湊拨与;
適配對象不再需要實現(xiàn)具體某個protocol,代碼更為簡潔艾猜。
2 多線程與block
GCD與Block
使用 dispatch_async系列方法买喧,可以以指定的方式執(zhí)行block
dispatch_async的完整定義
void dispatch_async(
dispatch_queue_t queue,
dispatch_block_t block);
功能:在指定的隊列里提交一個異步執(zhí)行的block捻悯,不阻塞當(dāng)前線程
通過queue來控制block執(zhí)行的線程。主線程執(zhí)行前文定義的 finishBlock對象
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