ios4.0系統(tǒng)已開始支持block,在編程過程中忍燥,block被Obj-C看成是對象墓卦,它封裝了一段代碼掷贾,這段代碼可以在任何時(shí)候執(zhí)行睛榄。Block可以作為函數(shù)參數(shù)或者函數(shù)的返回值,而其本身又可以帶輸入?yún)?shù)或返回值想帅。它和傳統(tǒng)的函數(shù)指針很類似场靴,但是有區(qū)別:block是inline的,并且它對局部變量是只讀的港准。
1旨剥、block的定義
// 聲明和實(shí)現(xiàn)寫在一起,就像變量的聲明實(shí)現(xiàn) int a = 10;
int(^aBlock)(int,int) = ^(int num1,int num2) {
return num1 * num2;
};
?// 聲明和實(shí)現(xiàn)分開浅缸,就像變量先聲明后實(shí)現(xiàn)
int a;
a = 10;
?int(^cBlock)(int,int);
?cBlock = ^(int num1,int num2)
?{
?return num1 * num2;
?};
其中轨帜,定義了一個(gè)名字為aBlock的block對象,并攜帶了相關(guān)信息:
1衩椒、aBlock 有兩個(gè)形式參數(shù)蚌父,分別為int類型;
2毛萌、aBlock 的返回值為int 類型苟弛;
3、等式右邊就是block的具體實(shí)現(xiàn)阁将;
4嗡午、^ 帶邊block聲明和實(shí)現(xiàn)的標(biāo)示(關(guān)鍵字);
當(dāng)然冀痕,你可以定義其他形式的block。e.g:無返回值狸演,無形式參數(shù)等言蛇;
void(^bBlock)() = ^()
{
int a =10;
printf(num = %d,a);
};
2、block 訪問權(quán)限
block可以訪問局部變量宵距,但是不能修改腊尚。
int a =10;
int(^dBlock)(int) = ^(int num)
{
a++;//not work!
return num * a;
};
此處不能修改的原因是在編譯期間確定的满哪,編譯器編譯的時(shí)候把a(bǔ)的值復(fù)制到block作為一個(gè)新變量(假設(shè)是a'= 10)婿斥,此時(shí)a'和a是沒有關(guān)系的。
這個(gè)地方就是函數(shù)中的值傳遞哨鸭。如果要修改就要加關(guān)鍵字:__block或者static
__block int a =7;
int(^dBlock)(int) = ^(int num)
{
a++;// work民宿!
return num * a;
};
3、block的調(diào)用
block調(diào)用就像調(diào)用函數(shù)一樣像鸡。
int c = aBlock(10,10);
bBlock();
4活鹰、block 的應(yīng)用
假設(shè)我們熟悉代理遞值的話,對代理我們可能又愛有恨!我們先建立模型A頁面 push B頁面志群,如果把A頁面的值傳遞到B頁面着绷,屬性和單例傳值可以搞定!但是如果Pop過程中把B頁面的值傳遞到A頁面锌云,那就可以用單例或者代理了荠医!說到代理,我們要先聲明協(xié)議桑涎,創(chuàng)建代理彬向,很是麻煩。常常我們傳遞一個(gè)數(shù)值需要在兩個(gè)頁面間寫很多代碼石洗,這些代碼改變頁面的整體順序幢泼,可讀性也打了折扣。所以讲衫,此時(shí)缕棵,block是一種優(yōu)化方案!
block傳值得例子
一涉兽、在第二個(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語句塊的函數(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語句塊保存到本類的實(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;
}];
}
可以看到代碼中的注釋,系統(tǒng)告訴我們可以用[segue destinationViewController]來獲得新的視圖控制器乌奇,也就是我們說的第二個(gè)視圖控制器没讲。
這時(shí)候上面(第一步中)定義的那個(gè)方法起作用了,如果你寫一個(gè)[tfVC returnText按回車,系統(tǒng)會(huì)自動(dòng)提示出來一個(gè):
tfVC returnText:<#^(NSString *showText)block#>
的東西礁苗,我們只要在焦點(diǎn)上回車爬凑,就可以快速創(chuàng)建一個(gè)代碼塊了,大家可以試試寂屏。這在寫代碼的時(shí)候是非常方便的贰谣。
5娜搂、 block的內(nèi)存管理
block本身是像對象一樣可以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
{
[super viewDidLoad];
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 = [_block copy];
同理,特別需要注意的地方就是在把block放到集合類當(dāng)中去的時(shí)候疲扎,如果直接把生成的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)變量句柠,則相當(dāng)于Block對該變量的引用計(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ì)對內(nèi)部的對象進(jìn)行一次retain帽哑。也就是說缸榄,self會(huì)被retain一次。當(dāng)self釋放的時(shí)候祝拯,需要block釋放后才會(huì)對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);
};