
2.1 Blcoks概要
2.1.1 什么是Blocks
Blocks是C語(yǔ)言的擴(kuò)充功能——“帶有自動(dòng)變量(即局部變量)的匿名函數(shù)”。
使用Blocks可以不聲明C++和Objective-C類,也沒(méi)有使用靜態(tài)變量、靜態(tài)全局變量或全局變量時(shí)的問(wèn)題,僅用編程C語(yǔ)言函數(shù)的源代碼量即可使用帶有自動(dòng)變量值的匿名函數(shù)。
2.2 Blocks模式
2.2.1 Block語(yǔ)法
-
Block常量表達(dá)式:

??:
^ int (int count){
return count +1;
};
-
省略返回值類型的Block常量表達(dá)式:

省略返回值類型時(shí),如果表達(dá)式有return語(yǔ)句就返回該返回值的類型。如果表達(dá)式?jīng)]有return語(yǔ)句就返回void類型。如果表達(dá)式有多個(gè)return語(yǔ)句,所有的return語(yǔ)句返回值的類型必須相同。
??:
^ 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語(yǔ)法下,可將Block語(yǔ)法賦值給聲明為Block類型的變量中(即源代碼中一旦使用Block語(yǔ)法就相當(dāng)于生成了可賦值給Block類型變量的“值”)。
??:
int (^blk) (int);
“Block”即指源代碼中的Block語(yǔ)法,也指由Block語(yǔ)法所生成的值。
-
使用Block語(yǔ)法將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í),可以通過(guò)typedef為Block類型提供別名,從而起到簡(jiǎn)化塊類型變量名的作用。
??:
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說(shuō)明符()
使用附有 __block說(shuō)明符的自動(dòng)變量可在Block中賦值該變量稱為 __block 變量。__block說(shuō)明符也被稱之為存儲(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說(shuō)明符。
-
截獲Objective-C對(duì)象,調(diào)用變更該對(duì)象的方法并不會(huì)產(chǎn)生編譯錯(cuò)誤,但是,向截獲的自動(dòng)變量(即所截獲的Objective-C對(duì)象)賦值則會(huì)產(chǎn)生錯(cuò)誤。
-
在使用C語(yǔ)言數(shù)組時(shí),Block中的截獲自動(dòng)變量的方法并沒(méi)有實(shí)現(xiàn)對(duì)C語(yǔ)言數(shù)組的截獲,需要通過(guò)指針實(shí)現(xiàn)對(duì)C語(yǔ)言數(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對(duì)閉包的對(duì)象實(shí)現(xiàn),簡(jiǎn)單說(shuō)來(lái),Block就是對(duì)象。
將Objective-C的代碼轉(zhuǎn)化為C++的代碼來(lái)理解Block的實(shí)現(xiàn)。
Objective-C 轉(zhuǎn) C++的方法:
- 在OC源文件block.m寫(xiě)好代碼。
- 打開(kāi)終端,cd到block.m所在文件夾。
- 輸入clang -rewrite-objc block.m,就會(huì)在當(dāng)前文件夾內(nèi)自動(dòng)生成對(duì)應(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; // 今后版本升級(jí)所需的區(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;
}
};
// 將來(lái)被調(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; // 今后版本升級(jí)所需的區(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)化而來(lái)的C++代碼為:
// 將來(lái)被調(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的值就是通過(guò)__main_block_impl_0構(gòu)造出來(lái)的。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; // 今后版本升級(jí)所需的區(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; // 今后版本升級(jí)所需的區(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類對(duì)象的結(jié)構(gòu)體
- _NSConcreteStackBlock相當(dāng)于Block的結(jié)構(gòu)體實(shí)例
也就是說(shuō)Block實(shí)質(zhì)是Objective-C對(duì)閉包的對(duì)象實(shí)現(xiàn),簡(jiǎn)單說(shuō)來(lái),Block就是對(duì)象。
Objective-C類與對(duì)象的實(shí)質(zhì)是什么呢?
Objective-C類與對(duì)象的實(shí)質(zhì):
Objective-C中的對(duì)象就是一個(gè)結(jié)構(gòu)體,并且所有的對(duì)象都有一個(gè)相同的結(jié)構(gòu)體(即Class是類,id是對(duì)象)。而且每一個(gè)對(duì)象都有一個(gè)isa指針,這個(gè)isa指向生成該對(duì)象的類。
Objective-C類與對(duì)象的實(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;
} ;
通過(guò)一個(gè)簡(jiǎn)單的MyObject類來(lái)說(shuō)明Objective-C類與對(duì)象的實(shí)質(zhì):
??:
@interface MyObject : NSObject
{
int val0;
int val1;
}
基于objc_object結(jié)構(gòu)體,該類的對(duì)象的結(jié)構(gòu)體如下:
struct MyObject {
Class isa; // 成員變量isa持有該類的結(jié)構(gòu)體實(shí)例指針
int val0; // 原先MyObject類的實(shí)例變量val0和val1被直接聲明為成員變量
int val1;
}
MyObject類的實(shí)例變量val0和val1被直接聲明為對(duì)象的成員變量?!癘bjective-C中由類生成對(duì)象”意味著,像該結(jié)構(gòu)體這樣“生成由該類生成的對(duì)象的結(jié)構(gòu)體實(shí)例”。生成的各個(gè)對(duì)象(即由該類生成的對(duì)象的各個(gè)結(jié)構(gòu)體實(shí)例),通過(guò)成員變量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í)庫(kù)所使用。
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語(yǔ)法表達(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語(yǔ)法表達(dá)式執(zhí)行之前就被聲明定義,所以,在Objective-C的源代碼中,執(zhí)行Block語(yǔ)法表達(dá)式時(shí)無(wú)需改動(dòng)便可使用截獲的自動(dòng)變量值。
2. "截獲自動(dòng)變量值"意味著在執(zhí)行Block語(yǔ)法時(shí),Block語(yǔ)法表達(dá)式所使用的自動(dòng)變量值被保存到Block的結(jié)構(gòu)體實(shí)例(即Block自身)中。
3. Block不能直接使用“C語(yǔ)言數(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ù)對(duì)由自動(dòng)變量追加的成員變量進(jìn)行初始化。即執(zhí)行Block語(yǔ)法使用的自動(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沒(méi)有使用的自動(dòng)變量不會(huì)被追加,如dmy變量。
注意2: 在初始化block結(jié)構(gòu)體實(shí)例時(shí),增加了被截獲的自動(dòng)變量,block的體積會(huì)變大。
- 執(zhí)行Block語(yǔ)法使用的自動(dòng)變量fmt和var都是從__cself里面獲取的,更說(shuō)明了二者是屬于block的。而且從注釋來(lái)看(注釋是由clang自動(dòng)生成的),這兩個(gè)變量是值傳遞,而不是指針傳遞,也就是說(shuō)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說(shuō)明符
Block中所使用的被截獲的自動(dòng)變量就如“帶有自動(dòng)變量值的匿名函數(shù)”所說(shuō),僅截獲自動(dòng)變量的值。Block中使用自動(dòng)變量后,在Block結(jié)構(gòu)體實(shí)例中重寫(xiě)該自動(dòng)變量也不會(huì)改變?cè)冉孬@的自動(dòng)變量。
為了在Block中保存值,有兩種方案:
- 改變存儲(chǔ)于特殊存儲(chǔ)區(qū)域的變量。
- 通過(guò)__block修飾符來(lái)改變。
1. 改變存儲(chǔ)于特殊存儲(chǔ)區(qū)域的變量
- 全局變量,可以直接訪問(wèn)。
- 靜態(tài)全局變量,可以直接訪問(wèn)。
- 靜態(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語(yǔ)法表達(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的指針對(duì)靜態(tài)變量進(jìn)行訪問(wèn)
2. 將靜態(tài)變量static_val的指針傳遞給__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù)并保存,這是超出作用域,使用靜態(tài)變量的最簡(jiǎn)單方法
*/
}
// 靜態(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ù),從這里開(kāi)始閱讀源代碼
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)變量沒(méi)有被截獲到block里面,它們的訪問(wèn)是不經(jīng)過(guò)block的(與__cself無(wú)關(guān)):
// 靜態(tài)函數(shù) __main_block_func_0 (Block語(yǔ)法表達(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的指針對(duì)靜態(tài)變量進(jìn)行訪問(wèn)
2. 將靜態(tài)變量static_val的指針傳遞給__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù)并保存,這是超出作用域,使用靜態(tài)變量的最簡(jiǎn)單方法
*/
}
-
訪問(wèn)靜態(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;
}
};
由上述可知, 超出作用域使用指針訪問(wèn)靜態(tài)變量的這種方法并不適用于自動(dòng)變量的訪問(wèn)。
實(shí)際上,在由Block語(yǔ)法生成的值Block上,可以存有超過(guò)其變量作用域的被截獲對(duì)象的自動(dòng)變量。變量作用域結(jié)束的同時(shí),原來(lái)的自動(dòng)變量被廢棄,因此Block中超過(guò)變量作用域而存在的變量,將不能通過(guò)指針訪問(wèn)原來(lái)的自動(dòng)變量。
2. 通過(guò)__block修飾符來(lái)改變。
__block說(shuō)明符用于指定將變量值設(shè)置到哪個(gè)存儲(chǔ)區(qū)域中,也就是說(shuō),當(dāng)自動(dòng)變量加上__block說(shuō)明符之后,會(huì)改變這個(gè)自動(dòng)變量的存儲(chǔ)區(qū)域。
__block說(shuō)明符用來(lái)指定Block中想變更值的自動(dòng)變量,加上__block之后的變量稱之為_(kāi)_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語(yǔ)法表達(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. 因此,通過(guò)__Block_byref_val_0結(jié)構(gòu)體實(shí)例的成員變量__forwarding可以訪問(wèn)該結(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變量,也就是說(shuō)原來(lái)單純的int類型的val變量被__block修飾后生成了一個(gè)結(jié)構(gòu)體。這個(gè)結(jié)構(gòu)體其中一個(gè)成員變量持有原來(lái)的val變量。
- __forwarding:通過(guò)__forwarding,可以實(shí)現(xiàn)無(wú)論__block變量配置在棧上還是堆上都能正確地訪問(wèn)__block變量,也就是說(shuō)__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í)例。而B(niǎo)lock共有三種類型。
Block與__block變量的實(shí)質(zhì):
圖片來(lái)自:《Objective-C高級(jí)編程:iOS與OS X多線程和內(nèi)存管理》

Block的類有三種:

注意:在 ARC 開(kāi)啟的情況下,將只會(huì)有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 類型的 block。
三種Block在內(nèi)存中的位置:

全局Block:_NSConcreteGlobalBlock
Block為_(kāi)NSConcreteGlobalBlock類對(duì)象(即Block配置在程序的數(shù)據(jù)區(qū)域中)的情況有兩種:
-
記述全局變量的地方有Block語(yǔ)法時(shí)??:
void (^blk)(void) = ^{printf("Global Block\n");};
int main()
{
blk();
}
這里通過(guò)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,說(shuō)明它是一個(gè)全局Block。
-
Block語(yǔ)法表達(dá)式中不使用“應(yīng)截獲的自動(dòng)變量”時(shí)
??:
int(^block)(int count) = ^(int count) {
return count;
};
block(2);
這里通過(guò)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 改寫(xiě)的具體實(shí)現(xiàn)方式和 LLVM 不太一樣,并且這里沒(méi)有開(kāi)啟 ARC。所以這里我們看到 isa 指向的還是_NSConcreteStackBlock。但在 LLVM 的實(shí)現(xiàn)中,開(kāi)啟 ARC 時(shí),block 應(yīng)該是 _NSConcreteGlobalBlock 類型
棧Block:_NSConcreteStackBlock
-
在生成Block以后,如果這個(gè)Block不是全局Block,那么它就是為_(kāi)NSConcreteStackBlock對(duì)象。
-
配置在全局區(qū)的block,從變量作用域外也可以通過(guò)指針安全地使用。但是設(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)無(wú)論__block變量配置在棧上還是堆上時(shí)都能夠正確地訪問(wèn)__block變量。圖片來(lái)自:《Objective-C高級(jí)編程: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也被廢棄。但該源代碼通過(guò)對(duì)應(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.
* 將通過(guò)Block語(yǔ)法生成的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對(duì)象
* 注冊(cè)到autoreleasepool中,然后返回該對(duì)象
*/
}
-
部分情況下向方法或函數(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 截獲對(duì)象
首先,我們來(lái)看一下生成并持有NSMutableArray類的對(duì)象的代碼??:
{
id array = [[NSMutableArray alloc] init];
}
變量作用域結(jié)束的同時(shí),附有__strong修飾符的變量array被立即釋放并廢棄
而在block里截獲array對(duì)象的代碼??:
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類的對(duì)象在Block的執(zhí)行部分超出其變量作用域而存在。即array超過(guò)了其作用域存在。
通過(guò)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類對(duì)象并被截獲的自動(dòng)變量array,是附有__strong修飾符的成員變量。在Objective-C中,C語(yǔ)言結(jié)構(gòu)體不能含有附有__strong修飾符的變量。因?yàn)榫幾g器不知道何時(shí)進(jìn)行C語(yǔ)言結(jié)構(gòu)體的初始化和廢棄操作,不能很好地管理內(nèi)存。
但是,Objective-C的運(yùn)行時(shí)庫(kù)能準(zhǔn)確把握Block從棧復(fù)制到堆以及堆上的Block被廢棄的時(shí)機(jī),因此Block的結(jié)構(gòu)體即時(shí)含有附有__stong修飾符或__weak修飾符的變量,也可以恰當(dāng)?shù)剡M(jìn)行初始化和廢棄。
在實(shí)現(xiàn)上是通過(guò)__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ù)包括使用指針全都沒(méi)有被調(diào)用。
- 而是,在Block從棧復(fù)制到堆時(shí)以及堆上的Block被廢棄時(shí)會(huì)調(diào)用這些函數(shù)。
調(diào)用copy函數(shù)和dispose函數(shù)的時(shí)機(jī)

2.3.7 __block變量和對(duì)象
__block可以指定任何類型的自動(dòng)變量。下面指定用于賦值Objective-C對(duì)象的id類型自動(dòng)變量:
__block id obj = [[NSObject alloc] init];
等同于:
// ARC有效時(shí),id類型以及對(duì)象類型變量默認(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截獲的對(duì)象。
當(dāng)堆上的Block被廢棄時(shí),使用_Block_object_dispose函數(shù),釋放Block截獲的對(duì)象。
-
在__block變量為“附有 __strong修飾符的id類型或?qū)ο箢愋妥詣?dòng)變量”的情形下會(huì)發(fā)生同樣的過(guò)程。
當(dāng)__block變量從棧復(fù)制到堆時(shí),使用_Block_object_copy函數(shù),持有賦值給__block變量的對(duì)象。
當(dāng)堆上的__block變量被廢棄時(shí),使用_Block_object_dispose函數(shù),釋放賦值給__block變量的對(duì)象。
由此可知:只要__block變量在堆上繼續(xù)存在,那么該對(duì)象就會(huì)繼續(xù)處于被持有的狀態(tài)。這與在Block中,對(duì)象賦值給“附有__strong修飾符的對(duì)象類型自動(dòng)變量”相同。
- 對(duì)象賦值給“附有__weak修飾符的id類型或?qū)ο箢愋蚠_block自動(dòng)變量”時(shí),__block變量對(duì)該對(duì)象持有弱引用。此時(shí)nil賦值在自動(dòng)變量上。
- 在使用附有__unsafe_unretained修飾符的變量時(shí),注意不要通過(guò)懸掛指針訪問(wèn)已被廢棄的對(duì)象,否則程序可能會(huì)崩潰!
- __autoreleasing修飾符與__block說(shuō)明符同時(shí)使用會(huì)產(chǎn)生編譯錯(cuò)誤。
2.3.8 Block循環(huán)引用
如果在Block內(nèi)部使用__strong修飾符的對(duì)象類型的自動(dòng)變量,那么當(dāng)Block從棧復(fù)制到堆的時(shí)候,該對(duì)象就會(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è)對(duì)象還同時(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
圖片來(lái)自:《Objective-C高級(jí)編程:iOS與OS X多線程和內(nèi)存管理》
- Block中沒(méi)有使用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語(yǔ)法中使用的obj_實(shí)際上截獲了self,而對(duì)編譯器來(lái)說(shuō),obj_只不過(guò)是對(duì)象的結(jié)構(gòu)體的成員變量。
blk_ = ^{NSLog(@"obj_ = %@", self->obj_);};
有兩種方法來(lái)解決Block循環(huán)引用:
- 使用__block變量避免循環(huán)引用
- 使用Block類型成員變量和附有__weak修飾符的對(duì)象(通常是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)引用
在生成并持有對(duì)象的狀態(tài)下會(huì)引起以下循環(huán)引用

如果不執(zhí)行execBlock實(shí)例方法,就會(huì)持續(xù)該循環(huán)引用從而造成內(nèi)存泄露。
通過(guò)執(zhí)行execBlock實(shí)例方法,Block被執(zhí)行,nil被賦值在__block變量blockSelf中,__block變量blockSelf對(duì)MyObject類對(duì)象的強(qiáng)引用失效,從而避免了循環(huán)引用
圖片來(lái)自:《Objective-C高級(jí)編程:iOS與OS X多線程和內(nèi)存管理》
使用__block變量避免循環(huán)引用的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 通過(guò)__block變量可控制對(duì)象的持有期間
- 在不能使用__weak修飾符的環(huán)境中使用
缺點(diǎn):
- 為避免循環(huán)引用必須執(zhí)行Block
使用Block類型成員變量和附有__weak修飾符的對(duì)象(通常是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修飾符來(lái)避免Block中的循環(huán)引用。ARC無(wú)效時(shí),__block說(shuō)明符被用來(lái)避免Block中的循環(huán)引用。
總結(jié)
Blocks是“帶有自動(dòng)變量(即局部變量)的匿名函數(shù)”。實(shí)質(zhì)是Objective-C對(duì)閉包的對(duì)象實(shí)現(xiàn),簡(jiǎn)單說(shuō)來(lái),Block就是對(duì)象。
在Blocks中,Block常量表達(dá)式會(huì)截獲所使用的自動(dòng)變量的值(即保存該自動(dòng)變量的瞬間值)。附有 __block說(shuō)明符的自動(dòng)變量可在Block中賦值。
棧上的Block會(huì)復(fù)制到堆時(shí),Block上的__block變量跟著復(fù)制到堆上。只要__block變量在堆上繼續(xù)存在,那么對(duì)象就會(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。


