Blocks are a non-standard extension added by Apple Inc. to Clang's implementations of the C, C++, and Objective-C programming languages that uses a lambda expression-like syntax to create closures within these languages. Blocks are supported for programs developed for Mac OS X 10.6+ and iOS 4.0+,although third-party runtimes allow use on Mac OS X 10.5 and iOS 2.2+ and non-Apple systems.
參照wikipedia琼稻,block吮螺,其實(shí)就是C、C++、OC中閉包的表達(dá)鸠补。
一萝风、block結(jié)構(gòu)剖析
如果非要想一句話來(lái)形容block的話,我會(huì)將其比作能截獲變量的函數(shù)紫岩。本節(jié)中规惰,我會(huì)用一個(gè)特別簡(jiǎn)單的block來(lái)進(jìn)行結(jié)構(gòu)剖析。
分析:
// 首先貼一段最基本的block使用被因,寫(xiě)在main.m里面
typedef int (^Block)(void);
int main(int argc, char * argv[]) {
// block實(shí)現(xiàn)
Block block = ^{
return 0;
};
// block調(diào)用
block();
return 0;
}
// 命令行在main.m文件夾執(zhí)行命令:clang -rewrite-objc main.m
// 得到main.cpp卿拴,我們看下main.cpp中與上述代碼直接相關(guān)的代碼(里面代碼很多)
// block內(nèi)部結(jié)構(gòu)體
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
typedef int (*Block)(void);
// block結(jié)構(gòu)體
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;
}
};
// block方法實(shí)現(xiàn)
static int __main_block_func_0(struct __main_block_impl_0 *__cself) {
return 0;
}
// block內(nèi)部結(jié)構(gòu)體
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)};
// main函數(shù)
int main(int argc, char * argv[]) {
// block實(shí)現(xiàn)
Block block = ((int (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
// block調(diào)用
((int (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
那么,我們從最下面的main函數(shù)看起梨与,分為兩個(gè)步驟堕花,分別對(duì)應(yīng)上面的代碼:
- block實(shí)現(xiàn):調(diào)用block結(jié)構(gòu)體的構(gòu)造函數(shù)_main_block_impl_0來(lái)實(shí)現(xiàn)block,可見(jiàn)粥鞋,傳入的兩個(gè)參數(shù)缘挽,分別是函數(shù)_main_block_func_0的指針和_main_block_desc_0結(jié)構(gòu)體。
- block調(diào)用:將block作為參數(shù)傳入block中的FuncPtr呻粹,也即_main_block_func_0方法壕曼,進(jìn)行調(diào)用。
兩個(gè)步驟相比較而言等浊,第二步比較簡(jiǎn)單腮郊,也符合一開(kāi)始的描述,將第一步中的block作為參數(shù)傳入FucPtr筹燕,就能訪問(wèn)block實(shí)現(xiàn)位置的上下文轧飞。那么,第一步中撒踪,我們需要看的就是block的結(jié)構(gòu)體过咬。看下面的注釋即可制妄。
// block結(jié)構(gòu)體
struct __main_block_impl_0 {
// impl結(jié)構(gòu)體掸绞,即block的主結(jié)構(gòu)體
struct __block_impl impl;
// Desc結(jié)構(gòu)體,即block的描述結(jié)構(gòu)體
struct __main_block_desc_0* Desc;
// block結(jié)構(gòu)體的構(gòu)造函數(shù)
__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;
}
};
// impl結(jié)構(gòu)體
struct __block_impl {
void *isa; // 存儲(chǔ)位置耕捞,_NSConcreteStackBlock衔掸、_NSConcreteGlobalBlock、_NSConcreteMallocBlock
int Flags; // 按位承載 block 的附加信息
int Reserved; // 保留變量
void *FuncPtr; // 函數(shù)指針砸脊,指向 Block 要執(zhí)行的函數(shù)具篇,即__main_block_func_0
};
// Desc結(jié)構(gòu)體
static struct __main_block_desc_0 {
size_t reserved; // 結(jié)構(gòu)體信息保留字段
size_t Block_size; // 結(jié)構(gòu)體大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
小結(jié):
由上文可以看出,block使用比函數(shù)多來(lái)兩個(gè)步驟凌埂,即使用構(gòu)造函數(shù)實(shí)現(xiàn)和使用FuncPtr的時(shí)候需要傳入block結(jié)構(gòu)體驱显。可以得出結(jié)論,block比函數(shù)多了一個(gè)功能埃疫,就是可以在實(shí)現(xiàn)的地方訪問(wèn)上文伏恐。
疑問(wèn):
1、好像并沒(méi)有提到block如何截獲變量栓霜。
2翠桦、isa表示的三種存儲(chǔ)位置_NSConcreteStackBlock、_NSConcreteGlobalBlock胳蛮、_NSConcreteMallocBlock之間有什么關(guān)系销凑。
二、截獲變量
在第一部分仅炊,留下了兩個(gè)疑問(wèn)斗幼,在本節(jié),我們首先看下變量的截獲問(wèn)題抚垄,這里有兩個(gè)關(guān)鍵點(diǎn)蜕窿,即截獲變量的block的結(jié)構(gòu)體分析和block截獲變量的時(shí)機(jī)。
1呆馁、截獲變量的block結(jié)構(gòu)體實(shí)現(xiàn)
// block截獲變量樣例代碼
typedef int (^Block)(void);
int main(int argc, char * argv[]) {
int i = 0;
Block block = ^{
return i;
};
block();
return 0;
}
// 命令行在main.m文件夾執(zhí)行命令:clang -rewrite-objc main.m
// 得到main.cpp桐经,我們看下main.cpp中block的結(jié)構(gòu)體和main函數(shù)
// block結(jié)構(gòu)體
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// main函數(shù)
int main(int argc, char * argv[]) {
int i = 0;
Block block = ((int (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
((int (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
可見(jiàn),與無(wú)變量截獲的樣例浙滤,有兩個(gè)區(qū)別:
- block結(jié)構(gòu)體中多了一個(gè)變量i阴挣,這也是為什么說(shuō)block會(huì)截獲變量?至于纺腊,i的值如何初始化繼續(xù)看下面屯吊。
- block的構(gòu)造函數(shù)__main_block_impl_0中多了一個(gè)參數(shù)i和 : i(_i),參數(shù)i好理解摹菠,為了給i賦值,而 : i(_i)則是c++語(yǔ)法結(jié)構(gòu)體中const類型變量的初始化方式骗爆,即將參數(shù)_i賦值給block結(jié)構(gòu)體中的變量i次氨。
2、block截獲變量的時(shí)機(jī)
參見(jiàn)二.1中摘投,main函數(shù)實(shí)現(xiàn):((int (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i))煮寡。可以發(fā)現(xiàn)block在構(gòu)造的時(shí)刻犀呼,會(huì)將參數(shù)i傳入結(jié)構(gòu)體中進(jìn)行初始化幸撕,所以,block會(huì)在實(shí)現(xiàn)的地方截獲變量外臂,而截獲的變量的值也是實(shí)現(xiàn)時(shí)刻的變量值坐儿。
三、截獲變量的類型
引用官方文檔:
1、Global variables are accessible, including static variables that exist within the enclosing lexical scope.
2貌矿、Parameters passed to the block are accessible (just like parameters to a function).
3炭菌、Stack (non-static) variables local to the enclosing lexical scope are captured as const variables.Their values are taken at the point of the block expression within the program. In nested blocks, the value is captured from the nearest enclosing scope.
4、Variables local to the enclosing lexical scope declared with the _block storage modifier are provided by reference and so are mutable.Any changes are reflected in the enclosing lexical scope, including any other blocks defined within the same enclosing lexical scope. These are discussed in more detail in The _block Storage Type.
5逛漫、Local variables declared within the lexical scope of the block, which behave exactly like local variables in a function.Each invocation of the block provides a new copy of that variable. These variables can in turn be used as const
or by-reference variables in blocks enclosed within the block.
大致可以看出block可以中的外部變量一共有五種黑低,依次是全局變量、全局靜態(tài)變量酌毡、局部靜態(tài)變量克握、_block修飾的變量和一個(gè)局部變量(截獲后為const類型)。這里枷踏,先把_block剔除菩暗,看一下另外四種:
// block截獲變量樣例代碼
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
int a = 0;
static int b = 0;
int main(int argc, char * argv[]) {
static int c = 0;
int i = 0;
NSMutableArray *arr = [NSMutableArray array];
Block block = ^{
a = 1;
b = 1;
c = 1;
[arr addObject:@"1"];
return i;
};
block();
return 0;
}
這里有個(gè)問(wèn)題,如果繼續(xù)使用clang -rewrite-objc main.m會(huì)報(bào)錯(cuò)呕寝,因?yàn)榘薕C庫(kù)UIKit勋眯,所以使用xcrun -sdk iphonesimulator9.3 clang -rewrite-objc main.m,如果想深入了解請(qǐng)看clang -rewrite-objc的使用點(diǎn)滴下梢。
// block結(jié)構(gòu)體和構(gòu)造函數(shù)
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *c;
NSMutableArray *arr;
int i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_c, NSMutableArray *_arr, int _i, int flags=0) : c(_c), arr(_arr), i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// block實(shí)現(xiàn)的方法
static int __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *c = __cself->c; // bound by copy
NSMutableArray *arr = __cself->arr; // bound by copy
int i = __cself->i; // bound by copy
a = 1;
b = 1;
(*c) = 1;
((void (*)(id, SEL, ObjectType))(void *)objc_msgSend)((id)arr, sel_registerName("addObject:"), (id)(NSString *)&__NSConstantStringImpl__var_folders_k__6b9p9yt96y9dq8ds8_kvf3kh0000gn_T_main_3c9752_mi_0);
return i;
}
// mian函數(shù)
int main(int argc, char * argv[]) {
static int c = 0;
int i = 0;
NSMutableArray *arr = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));
Block block = ((int (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &c, arr, i, 570425344));
((int (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
上述樣例中一共有五種外部變量客蹋,這里分為三類。
1孽江、全局變量a讶坯、和靜態(tài)全局變量b為第一類,block可以直接修改岗屏,沒(méi)什么要說(shuō)的辆琅。
2、局部靜態(tài)變量c这刷,從block構(gòu)造函數(shù)可以看出此處傳入的是c的指針婉烟,所以可以更改c。
3暇屋、局部基本變量i似袁,局部對(duì)象arr,可以發(fā)現(xiàn)i不能修改咐刨,而arr因?yàn)槭菍?duì)象昙衅,可以對(duì)對(duì)象進(jìn)行操作(但不能對(duì)給arr重新指向其他地址),這時(shí)需要注意的是定鸟,block內(nèi)部的i和arr均為const類型而涉,所以均不能對(duì)其進(jìn)行修改。
本節(jié)過(guò)后联予,大家對(duì)截獲變量大概只有兩個(gè)難點(diǎn)了啼县,即為_(kāi)block修飾的變量是什么意思和對(duì)上述第三種類型的變量材原,我們?nèi)绾卧赽lock中修改。聰明的童鞋應(yīng)該想到了谭羔,兩個(gè)難點(diǎn)其實(shí)是一個(gè)問(wèn)題:_block就是為了解決block中修改const變量服務(wù)的华糖。且看下部分。
四瘟裸、_block作用
三中留下了截獲變量的一個(gè)疑點(diǎn)客叉,即如何修改const類型的變量,其實(shí)话告,我們可以從局部靜態(tài)變量和局部對(duì)象[arr addObject:@"1"]中找到一些思路兼搏,即不直接操作變量,而是通過(guò)操縱其指針沙郭,雖然我們不能將指針指向新地址佛呻,但是,我們可以對(duì)指針空間進(jìn)行操作病线。
// 使用_block的樣例
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
int main(int argc, char * argv[]) {
__block int i = 2;
__block NSMutableArray *arr = [NSMutableArray array];
Block block = ^{
i = 1;
arr = [NSMutableArray array];
return i;
};
block();
return 0;
}
// 執(zhí)行xcrun -sdk iphonesimulator9.3 clang -rewrite-objc main.m后代碼
// __block為變量i創(chuàng)建的結(jié)構(gòu)體吓著,其中成員i為i的值,forwarding為指向自己的指針
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;
};
// __block為變量arr創(chuàng)建的結(jié)構(gòu)體送挑,其中成員arr為arr的值绑莺,forwarding為指向自己的指針
struct __Block_byref_arr_1 {
void *__isa;
__Block_byref_arr_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSMutableArray *arr;
};
// block結(jié)構(gòu)體
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // by ref
__Block_byref_arr_1 *arr; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, __Block_byref_arr_1 *_arr, int flags=0) : i(_i->__forwarding), arr(_arr->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// block函數(shù)實(shí)現(xiàn)
static int __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_i_0 *i = __cself->i; // bound by ref
__Block_byref_arr_1 *arr = __cself->arr; // bound by ref
(i->__forwarding->i) = 1;
(arr->__forwarding->arr) = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));
return (i->__forwarding->i);
}
// main函數(shù)
int main(int argc, char * argv[]) {
__attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 2};
__attribute__((__blocks__(byref))) __Block_byref_arr_1 arr = {(void*)0,(__Block_byref_arr_1 *)&arr, 33554432, sizeof(__Block_byref_arr_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"))};
Block block = ((int (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, (__Block_byref_arr_1 *)&arr, 570425344));
((int (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
從上述代碼,可以看出惕耕,無(wú)論是基本變量i還是對(duì)象arr都被_block變成了一個(gè)結(jié)構(gòu)體纺裁,而其值為結(jié)構(gòu)體里面的成員變量,那么我們也就能明白為什么能在block中對(duì)其進(jìn)行修改了司澎。當(dāng)然欺缘,這里有一個(gè)很大的疑問(wèn),即簡(jiǎn)單的結(jié)構(gòu)體就能完成在block里面修改外部變量的要求挤安,而這里為什么會(huì)多一個(gè)指向結(jié)構(gòu)體自己的_forwarding指針呢谚殊?且看下一部分。
// 有興趣可以看下下面這個(gè)樣例蛤铜,也能完成對(duì)i的修改络凿,那么問(wèn)題來(lái)了,__block的__forwarding指針到底有什么用處(i輸出為3)
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
int main(int argc, char * argv[]) {
int i = 1;
int *a = &i;
Block block = ^{
(*a)++;
return 0;
};
i ++;
block();
NSLog(@"%d", i);
}
五昂羡、block的存儲(chǔ)類型
在二、三摔踱、四節(jié)中基本解答了一中提出的截獲變量的問(wèn)題(留下了一個(gè)_forwarding指針問(wèn)題虐先,本節(jié)解答),那么從本節(jié)開(kāi)始將解答一中留下的另外一個(gè)問(wèn)題派敷,即isa表示的三種存儲(chǔ)位置_NSConcreteStackBlock蛹批、_NSConcreteGlobalBlock撰洗、_NSConcreteMallocBlock之間有什么關(guān)系。
1腐芍、_NSConcreteGlobalBlock
// a差导、全局聲明實(shí)現(xiàn)的block
// clang編譯后可發(fā)現(xiàn)isa指針為_(kāi)NSConcreteGlobalBlock
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
Block globalBlock = ^{
return 0;
};
int main(int argc, char * argv[]) {
globalBlock();
return 0;
}
// b、局部block未截取任何變量
// clang編譯后可發(fā)現(xiàn)isa指針為_(kāi)NSConcreteStackBlock猪勇,這就看出了clang的不準(zhǔn)確的地方设褐,從此以后,我們通過(guò)NSLog打印泣刹。此處NSLog打印出的為_(kāi)NSConcreteGlobalBlock助析。
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
int main(int argc, char * argv[]) {
Block globalBlock = ^ {
return 0;
};
globalBlock();
NSLog(@"%@", globalBlock);
return 0;
}
2、_NSConcreteStackBlock
ARC環(huán)境下椅您,大部分情況下編譯器通常會(huì)將創(chuàng)建在棧上的 block 自動(dòng)拷貝到堆上外冀,只有當(dāng)block 作為方法或函數(shù)的參數(shù)傳遞時(shí),編譯器可能不會(huì)自動(dòng)調(diào)用 copy 方法掀泳。
當(dāng) block 作為參數(shù)被傳入方法名帶有 usingBlock的 Cocoa Framework 方法或 GCD 的 API 時(shí)雪隧。這些方法會(huì)在內(nèi)部對(duì)傳遞進(jìn)來(lái)的 block 調(diào)用 copy或 _Block_copy進(jìn)行拷貝。
// 下述是獅子書(shū)上非常經(jīng)典的樣例员舵,會(huì)在Block block = [arr objectAtIndex:1];處crash脑沿,因?yàn)間etBlockArray中的block存儲(chǔ)在棧上,已經(jīng)被回收固灵。
// 這里需要注意的是一定是在參數(shù)上定義的block捅伤,若在其他地方定義,傳入?yún)?shù)中巫玻,則不會(huì)丛忆。
// 這里用的NSLog打印的block顯示_NSConcreteStackBlock。
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
id getBlockArray()
{
int val = 10;
NSLog(@"%@", ^{NSLog(@"blklog:%d", val);});
return [[NSArray alloc] initWithObjects:
^{NSLog(@"blk0:%d", val);},
^{NSLog(@"blk1:%d", val);}, nil];
}
int main(int argc, char * argv[]) {
id arr = getBlockArray();
Block block = [arr objectAtIndex:1];
block();
return 0;
}
// 解決方式仍秤,不多解釋了
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
id getBlockArray()
{
int val = 10;
NSLog(@"%@", ^{NSLog(@"blklog:%d", val);});
return [[NSArray alloc] initWithObjects:
[^{NSLog(@"blk0:%d", val) ;} copy],
[^{NSLog(@"blk1:%d", val);} copy], nil];
}
int main(int argc, char * argv[]) {
id arr = getBlockArray();
Block block = [arr objectAtIndex:1];
block();
return 0;
}
3熄诡、_NSConcreteMallocBlock
這里裝個(gè)逼:除了上述幾類block,其余的block在ARC環(huán)境下都是_NSConcreteMallocBlock類型诗力。歡迎打臉凰浮,那我就能學(xué)到更多知識(shí)了。哈哈苇本,開(kāi)個(gè)玩笑袜茧。仔細(xì)看的人可能會(huì)發(fā)現(xiàn),從一到五的clang信息中瓣窄,樣例中的block基本都是_NSConcreteStackBlock笛厦,其實(shí)不然,應(yīng)該都是_NSConcreteMallocBlock俺夕,你可以通過(guò)NSLog方式驗(yàn)證裳凸。
三種存儲(chǔ)類型不要在clang代碼中查看贱鄙,而是通過(guò)NSLog打印。
4姨谷、_block逗宁、_forwarding與堆
已知arc環(huán)境下,棧上的block會(huì)自動(dòng)copy到堆上梦湘,而block截取的_block變量也會(huì)同時(shí)copy到堆上(這塊就不細(xì)說(shuō)了瞎颗,可以去看獅子書(shū))。這就保證了践叠,即使截獲的變量出了作用域言缤,block也能訪問(wèn)到它,而在四中最后提到的方法則無(wú)法保證禁灼。如下樣例管挟。
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
// 聲明一個(gè)全局block
Block globalBlock;
void test() {
int i = 1;
int *a = &i;
Block block = ^{
(*a)++;
NSLog(@"%d", *a);
return 0;
};
i++;
block();
// 為全局block初始化
globalBlock = block;
}
int main(int argc, char * argv[]) {
test();
globalBlock();
}
// 大家心目中的輸出應(yīng)該為
**2016-12-15 17:30:21.188 block[8683:10811473] 3**
**2016-12-15 17:30:21.189 block[8683:10811473] 4**
// 實(shí)際輸出為
**2016-12-15 17:30:21.188 block[8683:10811473] 3**
**2016-12-15 17:30:21.189 block[8683:10811473] 24705**
為什么會(huì)出現(xiàn),這種結(jié)果呢弄捕,因?yàn)?a指向局部變量i僻孝,而i在出了test方法作用域后回收內(nèi)存,所以出現(xiàn)一個(gè)不知道哪來(lái)的數(shù)守谓。那么__block能處理這種情況嗎穿铆?
#import <UIKit/UIKit.h>
typedef int (^Block)(void);
Block globalBlock;
void test() {
__block int i = 1;
Block block = ^{
i ++;
NSLog(@"%d", i);
return 0;
};
i ++;
block();
globalBlock = block;
}
int main(int argc, char * argv[]) {
test();
globalBlock();
}
// 輸出
**2016-12-15 17:34:07.092 block[9046:10815940] 3**
**2016-12-15 17:34:07.094 block[9046:10815940] 4**
完美。這里看一下四中_block結(jié)構(gòu)體的構(gòu)造函數(shù)
// 由下可以看出斋荞,結(jié)構(gòu)體內(nèi)的i和arr都是由結(jié)構(gòu)體內(nèi)的_forwarding指針初始化的荞雏,這保證了block外部棧變量的獨(dú)立性,也保證了block截獲變量可以copy到堆上平酿,因?yàn)槭莀block類型凤优。
// 由四中的clang代碼還可以看出,無(wú)論block內(nèi)外蜈彼,訪問(wèn)i或者arr的方式均是i->__forwarding->i或arr->__forwarding->arr筑辨。這保證了block內(nèi)外訪問(wèn)的都是同一個(gè)內(nèi)存(這里指的堆上的內(nèi)存)。
__Block_byref_i_0 *_i, __Block_byref_arr_1 *_arr, int flags=0) : i(_i->__forwarding), arr(_arr->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
六幸逆、循環(huán)引用
文章到這也就收尾了棍辕,大家應(yīng)該也差不多知道了block會(huì)截獲變量,即會(huì)持有變量还绘,所以可能引起循環(huán)引用楚昭,關(guān)于循環(huán)引用我曾經(jīng)寫(xiě)過(guò)一篇文章,講的比較全面拍顷,參見(jiàn)循環(huán)引用抚太,看我就對(duì)了。
七菇怀、參考文獻(xiàn)
1凭舶、獅子書(shū):Objective-C高級(jí)編程 iOS與OS X多線程與內(nèi)存管理
2、https://www.zybuluo.com/MicroCai/note/51116
3爱沟、http://blog.tingyun.com/web/article/detail/845
4帅霜、https://en.wikipedia.org/wiki/Blocks_(C_language_extension)
文中部分__block均寫(xiě)成了_block,不要介意呼伸。如對(duì)您有所幫助身冀,喜歡一下,關(guān)注一下括享,謝謝~~