#Block
###Block概述
Block它是C語言級(jí)別和運(yùn)行時(shí)方面的一個(gè)特征鸯隅。Block封裝了一段代碼邏輯酵颁,也用{}括起蝎亚,和標(biāo)準(zhǔn)C語言中的函數(shù)/函數(shù)指針很相似澄暮,此外就是blokc能夠?qū)Χx環(huán)境中的變量可以引用到名段。這一點(diǎn)和其它各種語言中所說的“閉包”是非常類似的概念。在iOS中泣懊,block有很多應(yīng)用場景伸辟,比如對(duì)代碼封裝作為參數(shù)傳遞。這在使用dispatch并發(fā)(Operation中也有BlockOperation)和completion異步回調(diào)等處都廣泛應(yīng)用馍刮。
-Block是蘋果官方特別推薦使用的數(shù)據(jù)類型信夫,使用場景比較廣泛
-動(dòng)畫
-多線程
-集合遍歷
-網(wǎng)絡(luò)請(qǐng)求回調(diào)
-Block的作用
-用來保存某一段代碼,可以在恰當(dāng)時(shí)候再去出來調(diào)用
-功能類似于函數(shù)和方法
1.Block的基本使用
```
聲明:(返回類型)(^聲明的block名稱)(參數(shù)列表);
實(shí)現(xiàn):^(參數(shù)列表){代碼塊}
返回值類型(^block變量名)(參數(shù)列表) = ^(形參列表) {
}卡啰;
```
賦值的例子:
```objc
intsum(intvalue1,intvalue2)
{
returnvalue1 + value2;
}
intmain(intargc,constchar* argv[]) {
@autoreleasepool {
int(*sumP)(int,int);
sumP = sum;
NSLog(@"sum = %i",sumP(10,20));
int(^sumBlock) (intvalue1,intvalue2) = ^(intvalue1,intvalue2) {
returnvalue2 + value1;
};
NSLog(@"sum----%i",sumBlock(10,30));
}
return0;
}
```
其實(shí)Block和C語言中的指向函數(shù)的指針差不多
其實(shí)我們完全可以定義C語言的函數(shù)静稻,如下所示
```objc
intsum(intvalue1,intvalue2)
{
returnvalue1 + value2;
}
intmain(intargc,constchar* argv[]) {
@autoreleasepool {
int(*sumP)(int,int);
sumP = sum;
NSLog(@"sum = %i",sumP(10,20));
}
return0;
}
```
有時(shí)為了方便,用typedef給出定義匈辱,在蘋果的官方文檔里振湾,建議出現(xiàn)多次的block使用typedef定義。
而block的調(diào)用亡脸,則十分簡單:block名稱(參數(shù)列表);
2.使用中的注意點(diǎn)
-block中可以定義和外界同名的變量押搪,并且如果在block中定義了和外界同名的變量,在block中訪問的是block內(nèi)部的變量
-默認(rèn)情況下浅碾,在block內(nèi)部不能改變外面變量的值
-因?yàn)閎lock中變量和外面的變量并不是同一個(gè)變量大州,可以通過打打印變量的地址看出來
-如果block中訪問到了外界的變量,block會(huì)將外界變量拷貝一份到堆內(nèi)存中
-因?yàn)閎lock中使用的外面變量的值是拷貝過來的即值拷貝垂谢,所以在調(diào)用之前修改外界變量的值厦画,不會(huì)影響到block中拷貝的值
-如果想在block中修改外界變量的值,必須在外界變量前面加上`__block`
-如果在block中修改了外界變量的值埂陆,會(huì)影響到外界變量的值苛白,
我們可以把.m反編譯成c++文件,看看其內(nèi)部是怎么實(shí)現(xiàn)`__block`這個(gè)關(guān)鍵詞可以修改外部變量的
```cpp
intsum(intvalue1,intvalue2)
{
returnvalue1 + value2;
}
struct__main_block_impl_0 {
struct__block_impl impl;
struct__main_block_desc_0* Desc;
__main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,intflags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
staticint__main_block_func_0(struct__main_block_impl_0 *__cself,intvalue1,intvalue2) {
returnvalue2 + value1;
}
staticstruct__main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = {0,sizeof(struct__main_block_impl_0)};
intmain(intargc,constchar* argv[]) {
/* @autoreleasepool */{ __AtAutoreleasePool __autoreleasepool;
int(*sumP)(int,int);
sumP = sum;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_185299_mi_0,sumP(10,20));
int(*sumBlock) (intvalue1,intvalue2) = ((int(*)(int,int))&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_185299_mi_1,((int(*)(__block_impl *,int,int))((__block_impl *)sumBlock)->FuncPtr)((__block_impl *)sumBlock,10,30));
}
return0;
}
staticstructIMAGE_INFO {unsignedversion;unsignedflag; } _OBJC_IMAGE_INFO = {0,2};
```
加上__block
```cpp
intsum(intvalue1,intvalue2)
{
returnvalue1 + value2;
}
struct__Block_byref_a_0 {
void*__isa;
__Block_byref_a_0 *__forwarding;
int__flags;
int__size;
inta;
};
struct__main_block_impl_0 {
struct__block_impl impl;
struct__main_block_desc_0* Desc;
__Block_byref_a_0 *a;// by ref
__main_block_impl_0(void*fp,struct__main_block_desc_0 *desc, __Block_byref_a_0 *_a,intflags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
staticint__main_block_func_0(struct__main_block_impl_0 *__cself,intvalue1,intvalue2) {
__Block_byref_a_0 *a = __cself->a;// bound by ref
(a->__forwarding->a) =20;
returnvalue2 + value1;
}
staticvoid__main_block_copy_0(struct__main_block_impl_0*dst,struct__main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a,8/*BLOCK_FIELD_IS_BYREF*/);}
staticvoid__main_block_dispose_0(struct__main_block_impl_0*src) {_Block_object_dispose((void*)src->a,8/*BLOCK_FIELD_IS_BYREF*/);}
staticstruct__main_block_desc_0 {
size_t reserved;
size_t Block_size;
void(*copy)(struct__main_block_impl_0*,struct__main_block_impl_0*);
void(*dispose)(struct__main_block_impl_0*);
} __main_block_desc_0_DATA = {0,sizeof(struct__main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
intmain(intargc,constchar* argv[]) {
/* @autoreleasepool */{ __AtAutoreleasePool __autoreleasepool;
int(*sumP)(int,int);
sumP = sum;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_9f1f9e_mi_0,sumP(10,20));
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a,0,sizeof(__Block_byref_a_0),10};
int(*sumBlock) (intvalue1,intvalue2) = ((int(*)(int,int))&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a,570425344));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_9f1f9e_mi_1,((int(*)(__block_impl *,int,int))((__block_impl *)sumBlock)->FuncPtr)((__block_impl *)sumBlock,10,30));
}
return0;
}
staticstructIMAGE_INFO {unsignedversion;unsignedflag; } _OBJC_IMAGE_INFO = {0,2};
```
很明顯加上了__block之后傳入的是&這樣的焚虱,是把地址傳到block內(nèi)部去了,所以可以修改外部變量了
-block是存儲(chǔ)在堆中還是棧中呢
-默認(rèn)情況下block是存儲(chǔ)在棧中的懂版,如果對(duì)block進(jìn)行一個(gè)copy操作鹃栽,block會(huì)轉(zhuǎn)移到堆中
-如果block在棧中,block中訪問了外界的對(duì)象,那么不會(huì)對(duì)對(duì)象進(jìn)行retain操作
-但是如果block在堆中民鼓,block中訪問了外面的對(duì)象薇芝,那么會(huì)對(duì)外界的對(duì)象進(jìn)行一次retain操作
-如果block中訪問了外界的對(duì)象,一定要給對(duì)象加上__block丰嘉,只要加上__block夯到,哪怕block在堆中,也不會(huì)對(duì)外界的對(duì)象進(jìn)行retain
-想把block弄到堆中可以使用`Clock_copy()`
###有如下一些注意點(diǎn):
對(duì)定義環(huán)境的變量使用饮亏。默認(rèn)是以const的方式使用耍贾,這有點(diǎn)像函數(shù)的const參數(shù)傳遞,如過需要block內(nèi)修改可變路幸,則使用__block荐开,這樣做存儲(chǔ)就實(shí)現(xiàn)了共享,包括塊中的遞歸應(yīng)用和定義環(huán)境上下文中的多個(gè)block使用简肴。block通常定義在棧幀當(dāng)中晃听,而當(dāng)所處的棧幀被銷毀的時(shí)候,block以及引用到的__block變量將會(huì)依然有效砰识。
引用類型問題能扒。block中的引用默認(rèn)都是強(qiáng)引用,必要的時(shí)候需要使用__weak辫狼,同delegate使用的注意一樣初斑,避免循環(huán)引用。此外予借,蘋果文檔中還給出了對(duì)instanceVariable和對(duì)localVariable引用不同的例子越平,注意體會(huì)下。
copy灵迫。在類屬性中秦叛,要使用copy。此外瀑粥,對(duì)block進(jìn)行copy要使用Block_copy()/Block_release()挣跋。
蘋果文檔中幾種需要避免使用的方式∧唬《Blocks Programming Topics》中Using Blocks中的例子避咆,實(shí)際上就是要注意block定義的位置與其上下文的關(guān)系。
有關(guān)Block的地址/引用修噪。注意這篇文章中最后例子中的問題:http://www.cnblogs.com/kesalin/archive/2013/04/30/ios_block.html我的理解是Block的地址發(fā)生了變化查库,最終的問題是對(duì)Block引用地址釋放時(shí)的野指針錯(cuò)誤。