#Block
Block概述
Block它是C語言級別和運行時方面的一個特征敏弃。Block封裝了一段代碼邏輯,也用{}括起,和標準C語言中的函數(shù)/函數(shù)指針很相似,此外就是blokc能夠?qū)Χx環(huán)境中的變量可以引用到聊训。這一點和其它各種語言中所說的“閉包”是非常類似的概念。在iOS中恢氯,block有很多應用場景魔眨,比如對代碼封裝作為參數(shù)傳遞。這在使用dispatch并發(fā)(Operation中也有BlockOperation)和completion異步回調(diào)等處都廣泛應用酿雪。
- Block是蘋果官方特別推薦使用的數(shù)據(jù)類型,使用場景比較廣泛
- 動畫
- 多線程
- 集合遍歷
- 網(wǎng)絡請求回調(diào)
- Block的作用
- 用來保存某一段代碼侄刽,可以在恰當時候再去出來調(diào)用
- 功能類似于函數(shù)和方法
- Block的基本使用
聲明:(返回類型)(^聲明的block名稱)(參數(shù)列表);
實現(xiàn):^(參數(shù)列表){代碼塊}
返回值類型 (^block變量名)(參數(shù)列表) = ^(形參列表) {
}指黎;
賦值的例子:
int sum(int value1, int value2)
{
return value1 + value2;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int (*sumP)(int, int);
sumP = sum;
NSLog(@"sum = %i",sumP(10,20));
int (^sumBlock) (int value1, int value2) = ^(int value1,int value2) {
return value2 + value1;
};
NSLog(@"sum----%i",sumBlock(10,30));
}
return 0;
}
其實Block和C語言中的指向函數(shù)的指針差不多
其實我們完全可以定義C語言的函數(shù),如下所示
int sum(int value1, int value2)
{
return value1 + value2;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int (*sumP)(int, int);
sumP = sum;
NSLog(@"sum = %i",sumP(10,20));
}
return 0;
}
有時為了方便州丹,用typedef給出定義醋安,在蘋果的官方文檔里,建議出現(xiàn)多次的block使用typedef定義墓毒。
而block的調(diào)用吓揪,則十分簡單:block名稱(參數(shù)列表);
- 使用中的注意點
- block中可以定義和外界同名的變量,并且如果在block中定義了和外界同名的變量所计,在block中訪問的是block內(nèi)部的變量
- 默認情況下柠辞,在block內(nèi)部不能改變外面變量的值
- 因為block中變量和外面的變量并不是同一個變量,可以通過打打印變量的地址看出來
- 如果block中訪問到了外界的變量主胧,block會將外界變量拷貝一份到堆內(nèi)存中
- 因為block中使用的外面變量的值是拷貝過來的即值拷貝叭首,所以在調(diào)用之前修改外界變量的值,不會影響到block中拷貝的值
- 如果想在block中修改外界變量的值踪栋,必須在外界變量前面加上
__block
- 如果在block中修改了外界變量的值焙格,會影響到外界變量的值,
我們可以把.m反編譯成c++文件夷都,看看其內(nèi)部是怎么實現(xiàn)__block
這個關(guān)鍵詞可以修改外部變量的
int sum(int value1, int value2)
{
return value1 + 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, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int value1, int value2) {
return value2 + value1;
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * 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) (int value1, int value2) = ((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));
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
加上__block
int sum(int value1, int value2)
{
return value1 + value2;
}
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
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, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int value1, int value2) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a) = 20;
return value2 + value1;
}
static void __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*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __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};
int main(int argc, const char * 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) (int value1, int value2) = ((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));
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
很明顯加上了__block之后傳入的是&這樣的眷唉,是把地址傳到block內(nèi)部去了,所以可以修改外部變量了
- block是存儲在堆中還是棧中呢
- 默認情況下block是存儲在棧中的,如果對block進行一個copy操作冬阳,block會轉(zhuǎn)移到堆中
- 如果block在棧中 蛤虐,block中訪問了外界的對象,那么不會對對象進行retain操作
- 但是如果block在堆中摩泪,block中訪問了外面的對象笆焰,那么會對外界的對象進行一次retain操作
- 如果block中訪問了外界的對象,一定要給對象加上__block见坑,只要加上__block嚷掠,哪怕block在堆中,也不會對外界的對象進行retain
- 想把block弄到堆中可以使用
Clock_copy()
有如下一些注意點:
對定義環(huán)境的變量使用荞驴。默認是以const的方式使用不皆,這有點像函數(shù)的const參數(shù)傳遞,如過需要block內(nèi)修改可變熊楼,則使用__block霹娄,這樣做存儲就實現(xiàn)了共享,包括塊中的遞歸應用和定義環(huán)境上下文中的多個block使用鲫骗。block通常定義在棧幀當中犬耻,而當所處的棧幀被銷毀的時候,block以及引用到的__block變量將會依然有效执泰。
引用類型問題枕磁。block中的引用默認都是強引用,必要的時候需要使用__weak术吝,同delegate使用的注意一樣计济,避免循環(huán)引用。此外排苍,蘋果文檔中還給出了對instanceVariable和對localVariable引用不同的例子沦寂,注意體會下。
copy淘衙。在類屬性中传藏,要使用copy。此外彤守,對block進行copy要使用Block_copy()/Block_release()漩氨。
蘋果文檔中幾種需要避免使用的方式∫旁觯《Blocks Programming Topics》中Using Blocks中的例子叫惊,實際上就是要注意block定義的位置與其上下文的關(guān)系。
有關(guān)Block的地址/引用做修。注意這篇文章中最后例子中的問題:http://www.cnblogs.com/kesalin/archive/2013/04/30/ios_block.html我的理解是Block的地址發(fā)生了變化霍狰,最終的問題是對Block引用地址釋放時的野指針錯誤抡草。