2.1 Blcoks概要
2.1.1 什么是Blocks
Blocks是C語言的擴(kuò)充功能——“帶有自動(dòng)變量(即局部變量)的匿名函數(shù)”结耀。
使用Blocks可以不聲明C++和Objective-C類牙勘,也沒有使用靜態(tài)變量魂那、靜態(tài)全局變量或全局變量時(shí)的問題劝篷,僅用編程C語言函數(shù)的源代碼量即可使用帶有自動(dòng)變量值的匿名函數(shù)。
2.2 Blocks模式
2.2.1 Block語法
-
Block常量表達(dá)式:
??:
^ int (int count){
return count +1;
};
-
省略返回值類型的Block常量表達(dá)式:
省略返回值類型時(shí)日川,如果表達(dá)式有return語句就返回該返回值的類型辣垒。如果表達(dá)式?jīng)]有return語句就返回void類型。如果表達(dá)式有多個(gè)return語句,所有的return語句返回值的類型必須相同傀蚌。
??:
^ int (int count){
return count +1;
};
省略int類型返回值:
^ (int count){
return count +1;
};
??:
^ void (int count){
count +1;
NSLog(@"%@",count+1);
};
省略void類型:
^ (int count){
count +1;
NSLog(@"%@",count+1);
};
-
省略返回值類型和參數(shù)列表的Block常量表達(dá)式:
如果不使用參數(shù)基显,參數(shù)列表也可省略。
??:
^ void (void){
NSLog(@"Hello world");
};
省略void類型:
^ {
NSLog(@"Hello world");
};
2.2.2 Block類型變量
在Block語法下喳张,可將Block語法賦值給聲明為Block類型的變量中(即源代碼中一旦使用Block語法就相當(dāng)于生成了可賦值給Block類型變量的“值”)续镇。
??:
int (^blk) (int);
“Block”即指源代碼中的Block語法,也指由Block語法所生成的值销部。
-
使用Block語法將Block賦值為Block類型變量摸航。
??:
int (^blk) (int) = ^ int (int count){
return count +1;
};
-
由Block類型變量向Block類型變量賦值。
??:
int (^blk1) (int) = blk;
int (^blk2) (int);
blk2 = blk1;
-
Block類型變量作為函數(shù)參數(shù)傳遞舅桩。
??:
-(void)blockFunc:(int (^)(int))blk;
-
Block類型變量作為函數(shù)返回值返回酱虎。
??:
-(int)blockFunc1{
int (^blk) (int) = ^(int count){
return count +1;
};
return blk(1);
}
//等同于:
-(int)blockFunc1{
return ^(int count){
return count +1;
}(1);
}
-
Block類型變量作為函數(shù)參數(shù)和返回值時(shí),可以通過typedef為Block類型提供別名擂涛,從而起到簡化塊類型變量名的作用读串。
??:
typedef int (^BLK) (int);//
-(void)blockFunc:(BLK)blk;//作為函數(shù)參數(shù)
-(int)blockFunc1{
BLK blk = ^(int count){
return count +1;
};
return blk(1);
}//作為返回值
2.2.3 截獲自動(dòng)變量值
Blocks中,Block常量表達(dá)式會(huì)截獲所使用的自動(dòng)變量的值(即保存該自動(dòng)變量的瞬間值)撒妈,從而在執(zhí)行塊時(shí)使用恢暖。
??:
{
int val = 10;
void (^blk)() = ^{
NSLog(@"%d",val);
};
val = 2;
blk();
}//輸出為10;不是2狰右;
2.2.4 __block說明符()
使用附有 __block說明符的自動(dòng)變量可在Block中賦值該變量稱為 __block 變量杰捂。__block說明符也被稱之為存儲(chǔ)類型修改符。
??:
{
__block int val = 10;
void (^blk)() = ^{
var = 1;
NSLog(@"%d",val);
};
val = 2;
blk();
}//輸出為1棋蚌;不是2或者10嫁佳;
2.2.5 截獲的自動(dòng)變量
-
如果給Block中截獲的自動(dòng)變量賦值,需要給截獲的自動(dòng)變量附加__block說明符谷暮。
-
截獲Objective-C對象蒿往,調(diào)用變更該對象的方法并不會(huì)產(chǎn)生編譯錯(cuò)誤,但是湿弦,向截獲的自動(dòng)變量(即所截獲的Objective-C對象)賦值則會(huì)產(chǎn)生錯(cuò)誤瓤漏。
-
在使用C語言數(shù)組時(shí),Block中的截獲自動(dòng)變量的方法并沒有實(shí)現(xiàn)對C語言數(shù)組的截獲颊埃,需要通過指針實(shí)現(xiàn)對C語言數(shù)組自動(dòng)變量的截獲赌蔑。
??:
const char *text = "hello";
void (^blk)(void) = ^{
printf("%c\n", text[2]);
};
blk();
2.3 Blocks的實(shí)現(xiàn)
2.3.1 Block的實(shí)質(zhì)
Block實(shí)質(zhì)是Objective-C對閉包的對象實(shí)現(xiàn),簡單說來竟秫,Block就是對象。
將Objective-C的代碼轉(zhuǎn)化為C++的代碼來理解Block的實(shí)現(xiàn)跷乐。
Objective-C 轉(zhuǎn) C++的方法:
- 在OC源文件block.m寫好代碼肥败。
- 打開終端,cd到block.m所在文件夾。
- 輸入clang -rewrite-objc block.m馒稍,就會(huì)在當(dāng)前文件夾內(nèi)自動(dòng)生成對應(yīng)的block.cpp文件皿哨。
Objective-C中Block的實(shí)現(xiàn):
int main()
{
void (^blk)(void) = ^{
printf("Block\n");
};
blk();
return 0;
}
轉(zhuǎn)化為C++代碼:
// 結(jié)構(gòu)體 __block_impl
struct __block_impl {
void *isa;
int Flags; // 標(biāo)志
int Reserved; // 今后版本升級所需的區(qū)域
void *FuncPtr; // 函數(shù)指針
};
// block結(jié)構(gòu)體 __main_block_impl_0
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// Block的構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags = 0){
// _NSConcreteStackBlock用于初始化__block_impl結(jié)構(gòu)體的isa成員。(將Block指針賦值給Block的結(jié)構(gòu)體成員變量isa)
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 將來被調(diào)用的block內(nèi)部的代碼:block值被轉(zhuǎn)換為C的函數(shù)代碼
// __ceself為指向Block值的變量纽谒。
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
printf("Block\n");
}
// 靜態(tài)結(jié)構(gòu)體 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved; // 今后版本升級所需的區(qū)域
unsigned long Block_size; // Block的大小
} __mian_block_desc_0_DATA = { // 該結(jié)構(gòu)體實(shí)例的初始化部分
0,
sizeof(struct __main_block_impl_0) // 使用Block(即__main_block_impl_0結(jié)構(gòu)體實(shí)例)的大小進(jìn)行初始化
};
// main函數(shù)
int main()
{
// 調(diào)用結(jié)構(gòu)體__main_block_impl_0的構(gòu)造函數(shù)__main_block_impl_0
void (*blk)(void) =
(void (*)(void)) & __main_block_impl_0(
(void *)__main_block_func_0, &__mian_block_desc_0_DATA);
//調(diào)用block
((void (*)(struct __block_impl *))(
(struct __block_impl *)blk)->FuncPtr) ((struct __block_impl *)blk);
return 0;
}
其中证膨,Objective-C 中Block值轉(zhuǎn)化而來的C++代碼為:
// 將來被調(diào)用的block內(nèi)部的代碼:block值被轉(zhuǎn)換為C的函數(shù)代碼
// __ceself為指向Block值的變量。那么鼓黔,*__cself 就是是指向Block的值的指針央勒,也就相當(dāng)于是Block的值它自己(OC里的self)。
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
}
可以看出澳化,Block結(jié)構(gòu)體就是__main_block_impl_0結(jié)構(gòu)體崔步。Block的值就是通過__main_block_impl_0構(gòu)造出來的。Block結(jié)構(gòu)體的聲明:
// block結(jié)構(gòu)體 __main_block_impl_0
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// Block的構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags = 0){
// _NSConcreteStackBlock用于初始化__block_impl結(jié)構(gòu)體的isa成員缎谷。(將Block指針賦值給Block的結(jié)構(gòu)體成員變量isa)
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
其中井濒,__main_block_impl_0結(jié)構(gòu)體有三個(gè)部分:
- 第一個(gè)是成員變量impl,它是實(shí)際的函數(shù)指針列林,它指向__main_block_func_0瑞你。impl結(jié)構(gòu)體的聲明:
// 結(jié)構(gòu)體 __block_impl
struct __block_impl {
void *isa;
int Flags; // 標(biāo)志
int Reserved; // 今后版本升級所需的區(qū)域
void *FuncPtr; // 函數(shù)指針
};
- 第二個(gè)是成員變量是指向__main_block_desc_0結(jié)構(gòu)體的Desc指針,是用于描述當(dāng)前這個(gè)block的附加信息希痴。
// 靜態(tài)結(jié)構(gòu)體 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved; // 今后版本升級所需的區(qū)域
unsigned long Block_size; // Block的大小
} __mian_block_desc_0_DATA = { // 該結(jié)構(gòu)體實(shí)例的初始化部分
0,
sizeof(struct __main_block_impl_0) // 使用Block(即__main_block_impl_0結(jié)構(gòu)體實(shí)例)的大小進(jìn)行初始化
};
- 第三個(gè)部分是__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù)者甲,__main_block_impl_0 就是該 block 的實(shí)現(xiàn)。
// Block的構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags = 0){
// _NSConcreteStackBlock用于初始化__block_impl結(jié)構(gòu)體的isa成員润梯。(將Block指針賦值給Block的結(jié)構(gòu)體成員變量isa)
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
在這個(gè)結(jié)構(gòu)體的構(gòu)造函數(shù)里:
- isa指針保持這所屬類的結(jié)構(gòu)體的實(shí)例的指針过牙。
- __main_block_imlp_0結(jié)構(gòu)體就相當(dāng)于Objective-C類對象的結(jié)構(gòu)體
- _NSConcreteStackBlock相當(dāng)于Block的結(jié)構(gòu)體實(shí)例
也就是說Block實(shí)質(zhì)是Objective-C對閉包的對象實(shí)現(xiàn),簡單說來纺铭,Block就是對象寇钉。
Objective-C類與對象的實(shí)質(zhì)是什么呢?
Objective-C類與對象的實(shí)質(zhì):
Objective-C中的對象就是一個(gè)結(jié)構(gòu)體舶赔,并且所有的對象都有一個(gè)相同的結(jié)構(gòu)體(即Class是類,id是對象)竟纳。而且每一個(gè)對象都有一個(gè)isa指針撵溃,這個(gè)isa指向生成該對象的類。
Objective-C類與對象的實(shí)現(xiàn)中最基本的結(jié)構(gòu)體為objc_object結(jié)構(gòu)體和objc_class結(jié)構(gòu)體锥累。其中:
id類型是objc_object結(jié)構(gòu)體的指針類型缘挑。
typedef struct objc_object {
Class isa;
} *id;
- Class是objc_class結(jié)構(gòu)體的指針類型。
typedef struct objc_class *Class;
struct objc_class {
Class isa;
} ;
通過一個(gè)簡單的MyObject類來說明Objective-C類與對象的實(shí)質(zhì):
??:
@interface MyObject : NSObject
{
int val0;
int val1;
}
基于objc_object結(jié)構(gòu)體桶略,該類的對象的結(jié)構(gòu)體如下:
struct MyObject {
Class isa; // 成員變量isa持有該類的結(jié)構(gòu)體實(shí)例指針
int val0; // 原先MyObject類的實(shí)例變量val0和val1被直接聲明為成員變量
int val1;
}
MyObject類的實(shí)例變量val0和val1被直接聲明為對象的成員變量语淘』逵睿“Objective-C中由類生成對象”意味著,像該結(jié)構(gòu)體這樣“生成由該類生成的對象的結(jié)構(gòu)體實(shí)例”惶翻。生成的各個(gè)對象(即由該類生成的對象的各個(gè)結(jié)構(gòu)體實(shí)例)姑蓝,通過成員變量isa保持該類的結(jié)構(gòu)體實(shí)例指針。
各類的結(jié)構(gòu)體是基于objc_class結(jié)構(gòu)體的class_t結(jié)構(gòu)體:
struct class_t {
struct class_t *isa;
struct class_t *superclass;
Cache cache;
IMP *vtable;
uintptr_t data_NEVER_USE;
}
在Objective-C中吕粗,比如NSObject的class_t結(jié)構(gòu)體實(shí)例以及NSMutableArray的class_t結(jié)構(gòu)體實(shí)例等纺荧,均生成并保持各個(gè)類的class_t結(jié)構(gòu)體實(shí)例。
該實(shí)例持有聲明的成員變量颅筋、方法的名稱宙暇、方法的實(shí)現(xiàn)(即函數(shù)指針)、屬性以及父類的指針垃沦,并被Objective-C運(yùn)行時(shí)庫所使用客给。
2.3.2 截獲自動(dòng)變量值
使用Block的時(shí)候,不僅可以使用其內(nèi)部的參數(shù)肢簿,還可以使用Block外部的局部變量靶剑。而一旦在Block內(nèi)部使用了其外部變量,這些變量就會(huì)被Block保存池充。
Objective-C代碼:
int main()
{
int dmy = 256;
int val = 10;
const char *fmt = "var = %d\n";
void (^blk)(void) = ^{
printf(fmt,val);
};
val = 2;
fmt = "These values were changed. var = %d\n";
blk();
return 0;
}
轉(zhuǎn)化而成的C++代碼:
// 結(jié)構(gòu)體 __main_block_impl_0
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
const char *fmt; // Block語法表達(dá)式“使用的自動(dòng)變量”被追加到該結(jié)構(gòu)體
int val;
// 構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags = 0) : fmt(_fmt), val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 靜態(tài)函數(shù) __main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
const char *fmt = __cself->fmt;
int val = __cself->val;
/*
理解:
1. __main_block_impl_0結(jié)構(gòu)體實(shí)例(即Block)所截獲的自動(dòng)變量在Block語法表達(dá)式執(zhí)行之前就被聲明定義桩引,所以,在Objective-C的源代碼中收夸,執(zhí)行Block語法表達(dá)式時(shí)無需改動(dòng)便可使用截獲的自動(dòng)變量值坑匠。
2. "截獲自動(dòng)變量值"意味著在執(zhí)行Block語法時(shí),Block語法表達(dá)式所使用的自動(dòng)變量值被保存到Block的結(jié)構(gòu)體實(shí)例(即Block自身)中卧惜。
3. Block不能直接使用“C語言數(shù)組類型的自動(dòng)變量”厘灼,所以,截獲自動(dòng)變量時(shí)咽瓷,會(huì)將其值傳遞給結(jié)構(gòu)體的構(gòu)造函數(shù)進(jìn)行保存
*/
printf(fmt, val);
}
// 靜態(tài)結(jié)構(gòu)體 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
} __mian_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0)
};
int main()
{
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
// 調(diào)用結(jié)構(gòu)體__main_block_impl_0的構(gòu)造函數(shù)初始化該結(jié)構(gòu)體實(shí)例
void (*blk)(void) = &__main_block_impl_0(__main_block_func_0, &__mian_block_desc_0_DATA, fmt, val);
return 0;
}
- 在初始化結(jié)構(gòu)體實(shí)例時(shí)设凹,會(huì)根據(jù)傳遞給構(gòu)造函數(shù)的參數(shù)對由自動(dòng)變量追加的成員變量進(jìn)行初始化。即執(zhí)行Block語法使用的自動(dòng)變量(即截獲的自動(dòng)變量)fmt和val會(huì)初始化結(jié)構(gòu)體實(shí)例茅姜,被作為成員變量追加到了__main_block_impl_0結(jié)構(gòu)體中闪朱。
__main_block_impl_0結(jié)構(gòu)體如下:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
const char *fmt; //截獲的自動(dòng)變量
int val; //截獲的自動(dòng)變量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
注意1:block沒有使用的自動(dòng)變量不會(huì)被追加,如dmy變量钻洒。
注意2: 在初始化block結(jié)構(gòu)體實(shí)例時(shí),增加了被截獲的自動(dòng)變量奋姿,block的體積會(huì)變大。
- 執(zhí)行Block語法使用的自動(dòng)變量fmt和var都是從__cself里面獲取的素标,更說明了二者是屬于block的称诗。而且從注釋來看(注釋是由clang自動(dòng)生成的),這兩個(gè)變量是值傳遞头遭,而不是指針傳遞粪狼,也就是說Block僅僅截獲自動(dòng)變量的值退腥,所以這就解釋了即使改變了外部的自動(dòng)變量的值,也不會(huì)影響B(tài)lock內(nèi)部的值再榄。
函數(shù)體的代碼如下:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
const char *fmt = __cself->fmt; // bound by copy
int val = __cself->val; // bound by copy
printf(fmt,val);
}
2.3.3 __block說明符
Block中所使用的被截獲的自動(dòng)變量就如“帶有自動(dòng)變量值的匿名函數(shù)”所說,僅截獲自動(dòng)變量的值享潜。Block中使用自動(dòng)變量后困鸥,在Block結(jié)構(gòu)體實(shí)例中重寫該自動(dòng)變量也不會(huì)改變原先截獲的自動(dòng)變量。
為了在Block中保存值剑按,有兩種方案:
- 改變存儲(chǔ)于特殊存儲(chǔ)區(qū)域的變量疾就。
- 通過__block修飾符來改變。
1. 改變存儲(chǔ)于特殊存儲(chǔ)區(qū)域的變量
- 全局變量艺蝴,可以直接訪問猬腰。
- 靜態(tài)全局變量,可以直接訪問猜敢。
- 靜態(tài)變量姑荷,直接指針引用。(不適用)
Objective-C具體的實(shí)現(xiàn)的代碼:
int global_val = 1;//全局變量
static int static_global_val = 2;//全局靜態(tài)變量
int main()
{
static int static_val = 3;//靜態(tài)變量
void (^blk)(void) = ^{
global_val *=1;
static_global_val *=2;
static_val *=3;
};
return 0;
}
轉(zhuǎn)換成C++的代碼:
int global_val = 1; //全局變量
static int static_global_val = 2; // 靜態(tài)全局變量
// 結(jié)構(gòu)體 __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 結(jié)構(gòu)體 __main_block_impl_0
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *static_val; // 原先的源代碼中使用的靜態(tài)變量被追加為成員變量
// 構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 靜態(tài)函數(shù) __main_block_func_0 (Block語法表達(dá)式發(fā)生的轉(zhuǎn)換)
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
int *static_val = __cself->static_val;
global_val *= 1;
static_global_val *= 2;
(*static_val) *= 3;
/*
1. 使用靜態(tài)變量static_val的指針對靜態(tài)變量進(jìn)行訪問
2. 將靜態(tài)變量static_val的指針傳遞給__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù)并保存缩擂,這是超出作用域鼠冕,使用靜態(tài)變量的最簡單方法
*/
}
// 靜態(tài)結(jié)構(gòu)體 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
} __mian_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0)
};
// 主函數(shù),從這里開始閱讀源代碼
int main()
{
static int static_val = 3; // 靜態(tài)變量
// 調(diào)用__main_block_impl_0結(jié)構(gòu)體實(shí)例的構(gòu)造函數(shù)胯盯,并將靜態(tài)變量static_val的指針作為參數(shù)傳遞給構(gòu)造函數(shù)
blk = &__main_block_impl_0(__main_block_func_0, &__mian_block_desc_0_DATA, &static_val);
return 0;
}
-
全局變量和全局靜態(tài)變量沒有被截獲到block里面懈费,它們的訪問是不經(jīng)過block的(與__cself無關(guān)):
// 靜態(tài)函數(shù) __main_block_func_0 (Block語法表達(dá)式發(fā)生的轉(zhuǎn)換)
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
int *static_val = __cself->static_val;
global_val *= 1;
static_global_val *= 2;
(*static_val) *= 3;
/*
1. 使用靜態(tài)變量static_val的指針對靜態(tài)變量進(jìn)行訪問
2. 將靜態(tài)變量static_val的指針傳遞給__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù)并保存,這是超出作用域博脑,使用靜態(tài)變量的最簡單方法
*/
}
-
訪問靜態(tài)變量(static_val)時(shí)憎乙,將靜態(tài)變量的指針傳遞給__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù)并保存:
// 結(jié)構(gòu)體 __main_block_impl_0
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *static_val; // 原先的源代碼中使用的靜態(tài)變量被追加為成員變量
// 構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
由上述可知, 超出作用域使用指針訪問靜態(tài)變量的這種方法并不適用于自動(dòng)變量的訪問叉趣。
實(shí)際上泞边,在由Block語法生成的值Block上,可以存有超過其變量作用域的被截獲對象的自動(dòng)變量君账。變量作用域結(jié)束的同時(shí)繁堡,原來的自動(dòng)變量被廢棄,因此Block中超過變量作用域而存在的變量乡数,將不能通過指針訪問原來的自動(dòng)變量椭蹄。
2. 通過__block修飾符來改變。
__block說明符用于指定將變量值設(shè)置到哪個(gè)存儲(chǔ)區(qū)域中净赴,也就是說绳矩,當(dāng)自動(dòng)變量加上__block說明符之后,會(huì)改變這個(gè)自動(dòng)變量的存儲(chǔ)區(qū)域玖翅。
__block說明符用來指定Block中想變更值的自動(dòng)變量,加上__block之后的變量稱之為__block變量
Objective-C中__block變量具體的實(shí)現(xiàn)的代碼:
int main()
{
__block int val = 10;
void (^blk)(void) = ^{
val = 1;
};
return 0;
}
轉(zhuǎn)換而成的C++代碼:
// 結(jié)構(gòu)體 __Block_byref_val_0
struct __Block_byref_val_0 {
// 成員變量
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val; // 相當(dāng)于原自動(dòng)變量的成員變量
};
// 結(jié)構(gòu)體 __main_block_impl_0
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_val_0 *val; //“持有相當(dāng)于原自動(dòng)變量的成員變量”的“__main_block_impl_0結(jié)構(gòu)體實(shí)例”被追加到成員變量中
// 構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags =0) : val(_val->__forwarding){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 靜態(tài)函數(shù) __main_block_func_0 (Block語法表達(dá)式發(fā)生的轉(zhuǎn)換)
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
__Block_byref_val_0 *val = __cself->val;
(val->__forwarding->val) = 1;
/*
1. Block的__main_block_impl_0結(jié)構(gòu)體實(shí)例持有指向“__block變量的__Block_byref_val_0結(jié)構(gòu)體實(shí)例”的指針(即__Block_byref_val_0 *val)
2. __Block_byref_val_0結(jié)構(gòu)體實(shí)例的成員變量__forwarding持有指向”該實(shí)例自身“的指針
3. 因此翼馆,通過__Block_byref_val_0結(jié)構(gòu)體實(shí)例的成員變量__forwarding可以訪問該結(jié)構(gòu)體實(shí)例的成員變量val
*/
}
// 靜態(tài)函數(shù) __main_block_copy_0
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src){
_Block_object_assign(&dst->val, src->val, BLOCK_FIELD_IS_BYREF);
}
// 靜態(tài)函數(shù) __main_block_dispose_0
static void __main_block_dispose_0(struct __main_block_impl_0*src){
_Block_object_dispose(src->val, BLOCK_FIELD_IS_BYREF);
}
// 靜態(tài)結(jié)構(gòu)體 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __mian_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0),
__main_block_copy_0,
__main_block_dispose_0
};
// 主函數(shù)
int main()
{
__attribute__((__blocks__(byref))) __Block_byref_val_0 val = {
0,
&val,
0,
sizeof(__Block_byref_val_0),
10
};
/*
1. __block變量會(huì)變成__Block_byref_val_0結(jié)構(gòu)體類型的自動(dòng)變量(即棧上生成的__Block_byref_val_0結(jié)構(gòu)體實(shí)例)割以。
2. 該自動(dòng)變量被初始化為10,這個(gè)值也出現(xiàn)在結(jié)構(gòu)體實(shí)例的初始化中应媚,意味著該結(jié)構(gòu)體持有相當(dāng)于原自動(dòng)變量的成員變量严沥。
*/
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));
return 0;
}
- 結(jié)構(gòu)體__main_block_impl_0里面增加了一個(gè)成員變量,它是一個(gè)結(jié)構(gòu)體指針中姜,指向了__Block_byref_val_0結(jié)構(gòu)體的一個(gè)實(shí)例消玄。
注意1: __Block_byref_val_0結(jié)構(gòu)體并不在__main_block_impl_0結(jié)構(gòu)體中,目的是為了使得多個(gè)Block中使用 __block變量丢胚。
注意2:__Block_byref_val_0結(jié)構(gòu)體這個(gè)結(jié)構(gòu)體是變量val在被__block修飾后生成的翩瓜。
結(jié)構(gòu)體__main_block_impl_0的聲明:
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_val_0 *val; //“持有相當(dāng)于原自動(dòng)變量的成員變量”的“__main_block_impl_0結(jié)構(gòu)體實(shí)例”被追加到成員變量中
// 構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags =0) : val(_val->__forwarding){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 結(jié)構(gòu)體__Block_byref_val_0兩個(gè)成員變量需要特別注意:
- val:保存了最初的val變量,也就是說原來單純的int類型的val變量被__block修飾后生成了一個(gè)結(jié)構(gòu)體携龟。這個(gè)結(jié)構(gòu)體其中一個(gè)成員變量持有原來的val變量兔跌。
- __forwarding:通過__forwarding,可以實(shí)現(xiàn)無論__block變量配置在棧上還是堆上都能正確地訪問__block變量峡蟋,也就是說__forwarding是指向自身的坟桅。
結(jié)構(gòu)體__Block_byref_val_0的聲明:
// 結(jié)構(gòu)體 __Block_byref_val_0
struct __Block_byref_val_0 {
// 成員變量
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val; // 相當(dāng)于原自動(dòng)變量的成員變量
};
__forwarding成員變量的實(shí)現(xiàn) :
最初,block變量在棧上時(shí)层亿,它的成員變量forwarding指向棧上的__block變量結(jié)構(gòu)體實(shí)例桦卒。
在__block被復(fù)制到堆上時(shí),會(huì)將forwarding的值替換為堆上的目標(biāo)block變量用結(jié)構(gòu)體實(shí)例的地址匿又。而在堆上的目標(biāo)block變量自己的forwarding的值就指向它自己方灾。
2.3.4 Block存儲(chǔ)域
Block轉(zhuǎn)換為Block的結(jié)構(gòu)體類型的自動(dòng)變量,__block變量轉(zhuǎn)換為 __block的結(jié)構(gòu)體類型的自動(dòng)變量。
所謂結(jié)構(gòu)體類型的自動(dòng)變量碌更,即棧上生成的該結(jié)構(gòu)體的實(shí)例裕偿。而Block共有三種類型。
Block與__block變量的實(shí)質(zhì):
圖片來自:《Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理》
Block的類有三種:
注意:在 ARC 開啟的情況下痛单,將只會(huì)有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 類型的 block嘿棘。
三種Block在內(nèi)存中的位置:
全局Block:_NSConcreteGlobalBlock
Block為_NSConcreteGlobalBlock類對象(即Block配置在程序的數(shù)據(jù)區(qū)域中)的情況有兩種:
-
記述全局變量的地方有Block語法時(shí)??:
void (^blk)(void) = ^{printf("Global Block\n");};
int main()
{
blk();
}
這里通過clang轉(zhuǎn)換成的C++代碼中Block結(jié)構(gòu)體的聲明是:
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;//全局
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
Block結(jié)構(gòu)體構(gòu)造函數(shù)里面isa指針被賦予的是&_NSConcreteGlobalBlock,說明它是一個(gè)全局Block旭绒。
-
Block語法表達(dá)式中不使用“應(yīng)截獲的自動(dòng)變量”時(shí)
??:
int(^block)(int count) = ^(int count) {
return count;
};
block(2);
這里通過clang轉(zhuǎn)換成的C++代碼中Block結(jié)構(gòu)體的聲明是:
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = & _NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
Block結(jié)構(gòu)體構(gòu)造函數(shù)里面isa指針被賦予的是& _NSConcreteStackBlock
這里引用巧神的一段話:由于 clang 改寫的具體實(shí)現(xiàn)方式和 LLVM 不太一樣鸟妙,并且這里沒有開啟 ARC。所以這里我們看到 isa 指向的還是_NSConcreteStackBlock挥吵。但在 LLVM 的實(shí)現(xiàn)中重父,開啟 ARC 時(shí),block 應(yīng)該是 _NSConcreteGlobalBlock 類型
棧Block:_NSConcreteStackBlock
-
在生成Block以后忽匈,如果這個(gè)Block不是全局Block房午,那么它就是為_NSConcreteStackBlock對象。
-
配置在全局區(qū)的block丹允,從變量作用域外也可以通過指針安全地使用郭厌。但是設(shè)置在棧上的block袋倔,如果其作用域結(jié)束,該block就被銷毀折柠。
-
同樣的宾娜,由于__block變量也配置在棧上,如果其作用域結(jié)束液走,則該__block變量也會(huì)被銷毀碳默。
堆Block:_NSConcreteMallocBlock
但是,如果Block變量和__block變量復(fù)制到了堆上以后缘眶,則不再會(huì)受到變量作用域結(jié)束的影響了,因?yàn)樗兂闪硕袯lock髓废。
將棧block復(fù)制到堆以后巷懈,block結(jié)構(gòu)體的isa成員變量變成了_NSConcreteMallocBlock。
__block變量的結(jié)構(gòu)體成員變量__forwarding可以實(shí)現(xiàn)無論__block變量配置在棧上還是堆上時(shí)都能夠正確地訪問__block變量慌洪。圖片來自:《Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理》
ARC有效時(shí)顶燕,大多數(shù)情況下Block從棧上復(fù)制到堆上的代碼由編譯器實(shí)現(xiàn):
-
block作為函數(shù)值返回的時(shí)候
typedef int (^blk_t)(int);
blk_t func(int rate)
{
return ^(int count){return rate * count;};
}
該源代碼中的函數(shù)會(huì)返回配置在棧上的Block。即當(dāng)程序執(zhí)行從該函數(shù)返回函數(shù)調(diào)用方時(shí)冈爹,變量作用域結(jié)束涌攻,因此棧上的Block也被廢棄。但該源代碼通過對應(yīng)ARC的編譯器可轉(zhuǎn)換如下:
blk_t func(int rate)
{
blk_t tmp = &__func_block_impl_0(__func_block_func_0, &__func_block_desc_0_DATA, rate);
/*
* ARC有效時(shí)频伤,blk_t tmp 相當(dāng)于blk_t __strong tmp.
* 將通過Block語法生成的Block(即配置在棧上的Block結(jié)構(gòu)體實(shí)例)
* 賦值給相當(dāng)于Block類型的變量tmp
*/
tmp = objc_retainBlock(tmp);
/*
* objc_retainBlock實(shí)際上是_Block_copy函數(shù)
* 將棧上的Block復(fù)制到堆上
* 復(fù)制后恳谎,將堆上的地址作為指針賦值給變量tmp
*/
return objc_autoreleaseReturnValue(tmp);
/*
* 將堆上的Block作為Objective-C對象
* 注冊到autoreleasepool中,然后返回該對象
*/
}
-
部分情況下向方法或函數(shù)中傳遞block的時(shí)候
- Cocoa框架的方法而且方法名中含有usingBlock等時(shí)憋肖。
- Grand Central Dispatch 的API因痛。
除了以上情況,需要我們手動(dòng)復(fù)制block岸更。
2.3.5 __block變量存儲(chǔ)域
-
任何一個(gè)block被復(fù)制到堆上時(shí)鸵膏,__block變量也會(huì)一并從棧復(fù)制到堆上,并被該Block持有怎炊。
-
如果接著有其他Block被復(fù)制到堆上的話谭企,被復(fù)制的Block會(huì)持有__block變量,并增加block的引用計(jì)數(shù)评肆。
-
如果Block被廢棄债查,它所持有的__block也就被釋放(不再有block引用它)。
2.3.6 截獲對象
首先糟港,我們來看一下生成并持有NSMutableArray類的對象的代碼??:
{
id array = [[NSMutableArray alloc] init];
}
變量作用域結(jié)束的同時(shí)攀操,附有__strong修飾符的變量array被立即釋放并廢棄
而在block里截獲array對象的代碼??:
blk_t blk;
{
id array = [NSMutableArray new];
blk = [^(id object){
[array addObject:object];
NSLog(@"array count = %ld",[array count]);
} copy];
}
blk([NSObject new]);
blk([NSObject new]);
blk([NSObject new]);
其輸出:
array count = 1
array count = 2
array count = 3
賦值給變量array的NSMutableArray類的對象在Block的執(zhí)行部分超出其變量作用域而存在。即array超過了其作用域存在秸抚。
通過clang轉(zhuǎn)換成的C++代碼中Block結(jié)構(gòu)體的聲明:
// 結(jié)構(gòu)體 __main_block_impl_0
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
id __strong array;
// 構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id __strong_array, int flags =0) : array(_array){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
被NSMutableArray類對象并被截獲的自動(dòng)變量array速和,是附有__strong修飾符的成員變量歹垫。在Objective-C中,C語言結(jié)構(gòu)體不能含有附有__strong修飾符的變量颠放。因?yàn)榫幾g器不知道何時(shí)進(jìn)行C語言結(jié)構(gòu)體的初始化和廢棄操作排惨,不能很好地管理內(nèi)存。
但是碰凶,Objective-C的運(yùn)行時(shí)庫能準(zhǔn)確把握Block從棧復(fù)制到堆以及堆上的Block被廢棄的時(shí)機(jī)暮芭,因此Block的結(jié)構(gòu)體即時(shí)含有附有__stong修飾符或__weak修飾符的變量,也可以恰當(dāng)?shù)剡M(jìn)行初始化和廢棄欲低。
在實(shí)現(xiàn)上是通過__main_block_copy_0函數(shù)和__main_block_dispose_0函數(shù)進(jìn)行的:
// 靜態(tài)結(jié)構(gòu)體 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __mian_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0),
__main_block_copy_0,
__main_block_dispose_0
};
- __main_block_copy_0函數(shù)(copy函數(shù))和__main_block_dispose_0函數(shù)(dispose函數(shù))指針被賦值__main_block_desc_0結(jié)構(gòu)體成員變量copy和dispose中辕宏,但是在轉(zhuǎn)換后的源代碼中,這些函數(shù)包括使用指針全都沒有被調(diào)用砾莱。
- 而是瑞筐,在Block從棧復(fù)制到堆時(shí)以及堆上的Block被廢棄時(shí)會(huì)調(diào)用這些函數(shù)。
調(diào)用copy函數(shù)和dispose函數(shù)的時(shí)機(jī)
2.3.7 __block變量和對象
__block可以指定任何類型的自動(dòng)變量腊瑟。下面指定用于賦值Objective-C對象的id類型自動(dòng)變量:
__block id obj = [[NSObject alloc] init];
等同于:
// ARC有效時(shí)聚假,id類型以及對象類型變量默認(rèn)附加__strong修飾符。
__block id __strong obj = [[NSObject alloc] init];
由clang轉(zhuǎn)換成C++代碼:
/* __block變量的結(jié)構(gòu)體部分 */
// 結(jié)構(gòu)體 __Block_byref_obj_0
struct __Block_byref_obj_0 {
void *__isa;
__Block_byref_obj_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose_)(void*);
__strong id obj; // __block變量被追加為成員變量
};
// 靜態(tài)函數(shù) __Block_byref_id_object_copy_131
static void __Block_byref_id_object_copy_131(void *dst, void *src){
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
// 靜態(tài)函數(shù) __Block_byref_id_object_dispose_131
static void __Block_byref_id_object_dispose_131(void *src){
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
/* __block變量聲明部分 */
__Block_byref_obj_0 obj = {
0,
&obj,
0x20000000,
sizeof(__Block_byref_obj_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
[[NSObject alloc] init]
};
-
在Block中使用“附有__strong修飾符的id類型或?qū)ο箢愋妥詣?dòng)變量”的情況下:
當(dāng)Block從棧復(fù)制到堆時(shí)闰非,使用_Block_object_copy函數(shù)膘格,持有Block截獲的對象。
當(dāng)堆上的Block被廢棄時(shí)财松,使用_Block_object_dispose函數(shù)瘪贱,釋放Block截獲的對象。
-
在__block變量為“附有 __strong修飾符的id類型或?qū)ο箢愋妥詣?dòng)變量”的情形下會(huì)發(fā)生同樣的過程游岳。
當(dāng)__block變量從棧復(fù)制到堆時(shí)政敢,使用_Block_object_copy函數(shù),持有賦值給__block變量的對象胚迫。
當(dāng)堆上的__block變量被廢棄時(shí)喷户,使用_Block_object_dispose函數(shù),釋放賦值給__block變量的對象访锻。
由此可知:只要__block變量在堆上繼續(xù)存在褪尝,那么該對象就會(huì)繼續(xù)處于被持有的狀態(tài)。這與在Block中,對象賦值給“附有__strong修飾符的對象類型自動(dòng)變量”相同期犬。
- 對象賦值給“附有__weak修飾符的id類型或?qū)ο箢愋蚠_block自動(dòng)變量”時(shí)河哑,__block變量對該對象持有弱引用。此時(shí)nil賦值在自動(dòng)變量上龟虎。
- 在使用附有__unsafe_unretained修飾符的變量時(shí)璃谨,注意不要通過懸掛指針訪問已被廢棄的對象,否則程序可能會(huì)崩潰!
- __autoreleasing修飾符與__block說明符同時(shí)使用會(huì)產(chǎn)生編譯錯(cuò)誤佳吞。
2.3.8 Block循環(huán)引用
如果在Block內(nèi)部使用__strong修飾符的對象類型的自動(dòng)變量拱雏,那么當(dāng)Block從棧復(fù)制到堆的時(shí)候,該對象就會(huì)被Block所持有底扳。
何時(shí)棧上的Block會(huì)復(fù)制到堆:
- 調(diào)用Block的copy實(shí)例方法時(shí)
- Block作為函數(shù)返回值返回時(shí)
- 將Block賦值給附有__strong修飾符id類型的類或Block類型成員變量時(shí)
- 在方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時(shí)
如果這個(gè)對象還同時(shí)持有Block的話铸抑,就容易發(fā)生循環(huán)引用辆它。
- 使用Block類型成員變量和附有__strong修飾符的self出現(xiàn)循環(huán)引用
typedef void(^blk_t)(void);
@interface Person : NSObject
{
blk_t blk_;
}
@implementation Person
- (instancetype)init
{
self = [super init];
blk_ = ^{
NSLog(@"self = %@",self);
};
return self;
}
@end
Block blk_t持有self偿衰,而self也同時(shí)持有作為成員變量的blk_t
圖片來自:《Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理》
- Block中沒有使用self,但是截獲了self
@interface MyObject : NSObject
{
blk_t blk_;
id obj_;
}
@end
@implementation MyObject
- (id)init
{
self = [super init];
blk_ = ^{NSLog(@"obj_ = %@", obj_);};
return self;
}
Block語法中使用的obj_實(shí)際上截獲了self州丹,而對編譯器來說阱冶,obj_只不過是對象的結(jié)構(gòu)體的成員變量刁憋。
blk_ = ^{NSLog(@"obj_ = %@", self->obj_);};
有兩種方法來解決Block循環(huán)引用:
- 使用__block變量避免循環(huán)引用
- 使用Block類型成員變量和附有__weak修飾符的對象(通常是self)避免循環(huán)引用
使用__block變量避免循環(huán)引用
typedeft void (^blk_t)(void);
@interface MyObject : NSObject
{
blk_t blk_;
}
@end
@implementation MyObject
- (id)init
{
self = [super init];
__block id blockSelf = self;
blk_ = ^{
NSLog(@"self = %@", blockSelf);
blockSelf = nil; // 記得清零
};
return self;
}
- (void)execBlock
{
blk_();
}
- (void)dealloc
{
NSLog(@"dealloc");
}
@end
int main()
{
id o = [[MyObject alloc] init];
[o execBlock];
return 0;
}
如果不調(diào)用execBlock實(shí)例方法(即不執(zhí)行賦值給成員變量blk_的Block),便會(huì)循環(huán)引用并引起內(nèi)存泄露木蹬。
使用__block變量不恰當(dāng)會(huì)出現(xiàn)循環(huán)引用
在生成并持有對象的狀態(tài)下會(huì)引起以下循環(huán)引用
如果不執(zhí)行execBlock實(shí)例方法职祷,就會(huì)持續(xù)該循環(huán)引用從而造成內(nèi)存泄露。
通過執(zhí)行execBlock實(shí)例方法届囚,Block被執(zhí)行,nil被賦值在__block變量blockSelf中是尖,__block變量blockSelf對MyObject類對象的強(qiáng)引用失效意系,從而避免了循環(huán)引用
圖片來自:《Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理》
使用__block變量避免循環(huán)引用的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 通過__block變量可控制對象的持有期間
- 在不能使用__weak修飾符的環(huán)境中使用
缺點(diǎn):
- 為避免循環(huán)引用必須執(zhí)行Block
使用Block類型成員變量和附有__weak修飾符的對象(通常是self)避免循環(huán)引用
- (id)init
{
self = [super init];
__weak __typeof(&*self)weakSelf = self;
blk_ = ^{NSLog(@"self = %@", weakSelf);};
return self;
}
若由于Block引發(fā)了循環(huán)引用時(shí),在ARC有效時(shí)饺汹,使用__weak修飾符來避免Block中的循環(huán)引用蛔添。ARC無效時(shí),__block說明符被用來避免Block中的循環(huán)引用兜辞。
總結(jié)
Blocks是“帶有自動(dòng)變量(即局部變量)的匿名函數(shù)”迎瞧。實(shí)質(zhì)是Objective-C對閉包的對象實(shí)現(xiàn),簡單說來逸吵,Block就是對象凶硅。
在Blocks中,Block常量表達(dá)式會(huì)截獲所使用的自動(dòng)變量的值(即保存該自動(dòng)變量的瞬間值)扫皱。附有 __block說明符的自動(dòng)變量可在Block中賦值足绅。
棧上的Block會(huì)復(fù)制到堆時(shí),Block上的__block變量跟著復(fù)制到堆上韩脑。只要__block變量在堆上繼續(xù)存在氢妈,那么對象就會(huì)繼續(xù)處于被持有的狀態(tài)。
ARC有效時(shí)段多,大多數(shù)情況下Block從棧上復(fù)制到堆上的代碼由編譯器實(shí)現(xiàn):
- block作為函數(shù)值返回的時(shí)候
- 部分情況下向方法或函數(shù)中傳遞block的時(shí)候:
- Cocoa框架的方法而且方法名中含有usingBlock等時(shí)首量。
- Grand Central Dispatch 的API。