
前言
在<<iOS 與OS X多線程和內(nèi)存管理>>筆記:Blocks中我寫的都是我們?nèi)粘i_(kāi)發(fā)過(guò)程中所用到的Blocks.這里我們深層次的看一下Blocks的相關(guān)實(shí)現(xiàn).
把OC代碼轉(zhuǎn)換為C++結(jié)構(gòu)體代碼
為了使我們更方便看清Block內(nèi)部的運(yùn)行,我們需要把OC代碼代碼轉(zhuǎn)化為帶有結(jié)構(gòu)體的C++代碼.這里我們就需要使用到clang -rewrite-objc指令.步驟有如下兩步.
- 打開(kāi)終端,使用cd指令進(jìn)入需要轉(zhuǎn)化的文件目錄下,比如我要對(duì)桌面上的Test工程下的main.m文件進(jìn)行轉(zhuǎn)化.終端指令類似于下圖所示.


- 然后執(zhí)行如下的終端命令 clang -rewrite-objc main.m,如下所示.

然后在當(dāng)前文件夾下就會(huì)出現(xiàn)后綴為.cpp的C++執(zhí)行文件.如下所示.

Block的實(shí)現(xiàn)
首先,我們?cè)趍ain函數(shù)中寫一個(gè)簡(jiǎn)單block匿名函數(shù)并且進(jìn)行調(diào)用,如下所示.
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^blk)(void) = ^{printf("Block\n");};
blk();
}
return 0;
}
然后,我們通過(guò) clang -rewrite-objc main.m指令把mian.m轉(zhuǎn)變?yōu)镃++文件.里面代碼較多,我們下拉到文件的最底部.
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
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 void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
}
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;
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
return 0;
}
我們可以看到,我們寫的block已經(jīng)被轉(zhuǎn)化為一個(gè)C++語(yǔ)言的函數(shù),如下所示.
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
}
概念函數(shù)的參數(shù)__cself相當(dāng)于C++實(shí)例方法中指向?qū)嵗陨淼淖兞縯his,或是Objective-C實(shí)例方法中指向?qū)ο笞陨淼淖兞縮elf,也就是說(shuō)參數(shù)____cself為指向Block值的變量.可是我們發(fā)現(xiàn)____cself并沒(méi)有在這里使用,這里我們先不做研究,我們先看一下參數(shù)____cself的本質(zhì).
struct __main_block_impl_0 *__cself
-
Block的結(jié)構(gòu)體
我們看到參數(shù)____cself是__main_block_impl_0 結(jié)構(gòu)體的指針,該結(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;
}
};
通過(guò)<<iOS 與OS X多線程和內(nèi)存管理>>我們可以了解到兩個(gè)成員變量各包含什么信息.

-
Block結(jié)構(gòu)體的成員變量
我們先看一下成員變量impl的結(jié)構(gòu)體(在.cpp文件的頂部位置).如下所示.
struct __block_impl {
void *isa;
int Flags;
int Reserved;//今后版本升級(jí)所需的區(qū)域
void *FuncPtr;//函數(shù)指針
};
第二個(gè)成員變量Desc主要是存儲(chǔ)今后版本升級(jí)所需的區(qū)域和Block大小.具體如下所示.
static struct __main_block_desc_0 {
size_t reserved; //今后版本升級(jí)所需的區(qū)域
size_t Block_size; //Block大小
}
-
Block的構(gòu)造
接下來(lái)我們就看一下__main_block_impl_0的構(gòu)造函數(shù)是如何構(gòu)造的.在main函數(shù)中調(diào)用的源碼如圖所示.

書中為了方便大家理解這句代碼調(diào)用,進(jìn)行了如下的轉(zhuǎn)換.也就是說(shuō)blk其實(shí)上是指向類型為_(kāi)_main_block_impl_0的tmp結(jié)構(gòu)體指針.
struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;
接下來(lái)我們看一下結(jié)構(gòu)體的構(gòu)造函數(shù)的參數(shù).首先是__main_block_desc_0_DATA這個(gè)參數(shù).我們?cè)诖a中找到了它的賦值過(guò)程.如下所示.
static struct __main_block_desc_0 __main_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0)
};
通過(guò)上面的構(gòu)造函數(shù),__main_block_impl_0的值就會(huì)如下所示.
impl.isa = &_NSConcreteStackBlock;
impl.Flags = 0;
impl.Reserved = 0;
impl.FuncPtr = ___main_block_func_0;
Desc = &__main_block_desc_0_DATA;
-
Block的調(diào)用過(guò)程
接下來(lái)我們看一下使用block的代碼是如何實(shí)現(xiàn)的.
blk();
找到.cpp文件對(duì)應(yīng)的代碼如下所示.
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
我們?nèi)サ艮D(zhuǎn)化部分.簡(jiǎn)化代碼之后如下所示.這句代碼是什么意思呢?這就是使用函數(shù)的指針調(diào)用函數(shù).正如我們剛剛所示的一樣.正如上一個(gè)模塊所說(shuō)的那樣,___main_block_func_0的函數(shù)指針被賦值到了結(jié)構(gòu)體的FuncPtr中了.另外___main_block_func_0的所需參數(shù)是__main_block_impl_0的類型,也就是blk.所以有以下的函數(shù)調(diào)用.
(*blk->FuncPtr)(blk);
-
Block的實(shí)質(zhì)
這時(shí)候我們需要回過(guò)頭來(lái)說(shuō)明__main_block_impl_0結(jié)構(gòu)體成員變量 impl中的isa指針.

我們知道isa指針在構(gòu)造函數(shù)中被賦值為&_NSConcreteStackBlock.如下圖所示.

其實(shí)Block就是Objective-C對(duì)象.為什么這么說(shuō)呢?首先我們看一下什么叫做Objective-C對(duì)象.
在Objective-C中,任何類的定義都是對(duì)象。類和類的實(shí)例(對(duì)象)沒(méi)有任何本質(zhì)上的區(qū)別。任何對(duì)象都有isa指針。
假定我們創(chuàng)建一個(gè)如下的對(duì)象.
@interface MyObject : NSObject
{
int val0;
int val1;
}
@end
那么基于Objective-C對(duì)象的結(jié)構(gòu)體就應(yīng)該如下所示.
struct MyObject
{
Class isa;
int val0;
int val1;
}
其中的isa指針指向如下所示.具體可查看書中的98頁(yè).

通過(guò)比較我們知道Block的結(jié)構(gòu)體中有isa指針._NSConcreteStackBlock就相當(dāng)于上圖的class_t結(jié)構(gòu)體實(shí)例.也就是說(shuō)Block即為Objective-C的對(duì)象.
Block截獲自動(dòng)變量值的實(shí)現(xiàn)
對(duì)于Block截獲自動(dòng)變量值,在<<iOS 與OS X多線程和內(nèi)存管理>>筆記:Blocks中我們已經(jīng)說(shuō)過(guò)了,現(xiàn)在我們列舉一下例子.來(lái)看一下是如何實(shí)現(xiàn)截獲自動(dòng)變量值這一過(guò)程的.
int number = 1;
void (^blk)(void) = ^{
printf("value:%d\n",number);
};
number = 3;
blk();
運(yùn)行程序.打印結(jié)果如下所示.

通過(guò)clang -rewrite-objc main.m指令編譯成C++文件.其中核心代碼如下所示.
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int number;//新增成員變量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _number, int flags=0) : number(_number) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int number = __cself->number; // bound by copy
printf("value:%d\n",number);
}
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 number = 1;
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, number));
number = 3;
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
return 0;
}
這時(shí)候我們把Block的結(jié)構(gòu)體拿出來(lái)看一下.我們發(fā)現(xiàn)新增了一個(gè)成員變量number以及構(gòu)造方法發(fā)生新增了對(duì)number的賦值.
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int number;//新增成員變量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _number, int flags=0) : number(_number) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
然后看一下main函數(shù)中__main_block_impl_0構(gòu)造函數(shù)的構(gòu)造過(guò)程.
int number = 1;
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, number));
這一步我們就知道在__main_block_impl_0結(jié)構(gòu)體構(gòu)造的時(shí)候已經(jīng)把number的值存儲(chǔ)到了自身成員變量number中了,所以后面number如何改變,那么Block在構(gòu)造完成之后打印的number值就不會(huì)發(fā)生改變了.
通過(guò)上面的表述,我們可以就了解為什么在不能Block中直接修改變量的值?(面試題).例如下圖所示.

這是為什么呢?我們看一下__main_block_func_0函數(shù)的實(shí)現(xiàn),如下所示.我們可以知道傳遞的是__main_block_impl_0結(jié)構(gòu)體的成員變量的值.而不是指針(其實(shí)就算是指針也沒(méi)有任何的關(guān)系),跟原來(lái)的number變量無(wú)任何關(guān)系.所以我們不能在函數(shù)中直接修改number變量變量.
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int number = __cself->number; // bound by copy
printf("value:%d\n",number);
}
__block說(shuō)明符的實(shí)現(xiàn)
上面一個(gè)模塊最后我們說(shuō)到如果直接在block中給變量賦值會(huì)報(bào)錯(cuò),我們發(fā)現(xiàn)根本原因就是Block結(jié)構(gòu)體中傳遞的是變量值,而不是指針,那么如何解決這一問(wèn)題呢?這時(shí)候__block說(shuō)明符就出現(xiàn)了.我們看一下C語(yǔ)言代碼,如下所示.
__block int number = 1;
void (^blk)(void) = ^{
printf("value:%d\n",number);
number = 6;
};
blk();
但是通過(guò)clang -rewrite-objc main.m指令轉(zhuǎn)變的C++代碼去發(fā)生了很大的變化.核心代碼如下所示.
//numbr變量已經(jīng)通過(guò)__block的修飾變成了結(jié)構(gòu)體
struct __Block_byref_number_0 {
void *__isa;
__Block_byref_number_0 *__forwarding;
int __flags;
int __size;
int number;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_number_0 *number; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_number_0 *_number, int flags=0) : number(_number->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_number_0 *number = __cself->number; // bound by ref
printf("value:%d\n",(number->__forwarding->number));
(number->__forwarding->number) = 6;
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->number, (void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->number, 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;
__attribute__((__blocks__(byref))) __Block_byref_number_0 number = {(void*)0,(__Block_byref_number_0 *)&number, 0, sizeof(__Block_byref_number_0), 1};
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_number_0 *)&number, 570425344));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
return 0;
}
我們看一下主要改變的部分.int number = 1;變成__block int number = 1;之后,C++代碼如下所示.代碼量提升了不是一倍兩倍呀~
struct __Block_byref_number_0 {
void *__isa;
__Block_byref_number_0 *__forwarding;//指向自身的指針
int __flags;
int __size;
int number;
};
然后我們看一下在main函數(shù)中的構(gòu)造代碼.如下所示.
__attribute__((__blocks__(byref))) __Block_byref_number_0 number = {(void*)0,(__Block_byref_number_0 *)&number, 0, sizeof(__Block_byref_number_0), 1};
簡(jiǎn)化代碼之后,如下所示.
__Block_byref_number_0 number = {
0,
&number,
0,
sizeof(__Block_byref_number_0),
1
};
這時(shí)候Block結(jié)構(gòu)體的構(gòu)造函數(shù)和新增成員變量也發(fā)生了改變.成員變量變成了指向__Block_byref_number_0類型的結(jié)構(gòu)體.
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_number_0 *number; //新增成員變量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_number_0 *_number, int flags=0) : number(_number->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
那么在block中進(jìn)行賦值的時(shí)候是如何操作的呢?這主要是通過(guò)__Block_byref_number_0的成員變量__forwarding來(lái)完成的.__forwarding是指向本身的指針.我們可以通過(guò)__forwarding來(lái)找到成員變量number的值.所以在__main_block_func_0函數(shù)實(shí)現(xiàn)中有如下的代碼.
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_number_0 *number = __cself->number; // bound by ref
printf("value:%d\n",(number->__forwarding->number));
(number->__forwarding->number) = 6;
}
對(duì)于__Block_byref_number_0結(jié)構(gòu)體中的__forwarding指針,我們可以看下面的示意圖.

Block存儲(chǔ)域
通過(guò)下面一張表我們了解到Block和__block變量時(shí)存儲(chǔ)在棧區(qū)的結(jié)構(gòu)體類型自動(dòng)變量(一般情況下).
| 名稱 | 實(shí)質(zhì) |
|---|---|
| Block | 棧上Block的結(jié)構(gòu)體實(shí)例 |
| __block | 棧上__block變量的結(jié)構(gòu)體實(shí)例 |
接下來(lái)我們還是來(lái)研究Block結(jié)構(gòu)體的isa指針,在前面的例子中,isa指針是指向_NSConcreteStackBlock的.其實(shí)還有很多類似的類.我們先用一張表格來(lái)說(shuō)明每個(gè)類的不同點(diǎn)
| 類 | 設(shè)置對(duì)象的存儲(chǔ)域 | 副本源的配置存儲(chǔ)域 | 復(fù)制效果 |
|---|---|---|---|
| _NSConcreteStackBlock | 棧 | 棧 | 從棧區(qū)復(fù)制到堆區(qū) |
| _NSConcreteMallocBlock | 堆 | 堆 | 引用計(jì)數(shù)增加 |
| _NSConcreteGlobaBlock | 全局區(qū) | 全局區(qū) | 什么也不做 |
通過(guò)上面的表格,我們就可以知道兩個(gè)面試題的答案,
問(wèn): Block的類一共有幾種?
答: 三種,分別是 _NSConcreteStackBlock 、_NSConcreteMallocBlock、_NSConcreteGlobaBlock
問(wèn): Block為什么用copy修飾?
答: block在定義成屬性的時(shí)候應(yīng)該使用copy修飾,平常我們使用的block主要是存放在棧區(qū)的(有的也會(huì)存放在全局區(qū)).棧區(qū)的block出了作用域之后就會(huì)被釋放掉,如果我們?cè)赽lock釋放掉之后還繼續(xù)調(diào)用,那么就會(huì)出現(xiàn)crash.理論上,在全局區(qū)的block我們是不需要進(jìn)行copy的.但是大部分的block是存儲(chǔ)在棧區(qū)的,為了統(tǒng)一規(guī)范管理,所以我們都使用copy對(duì)block屬性進(jìn)行修飾.
__block變量存儲(chǔ)域
上一個(gè)模塊是對(duì)Block進(jìn)行了說(shuō)明,那么對(duì)于使用__block變量的Block從棧上復(fù)制到堆上是,__block變量會(huì)有什么影響呢?
| __block變量的配置存儲(chǔ)域 | Block從棧區(qū)復(fù)制到堆時(shí)的影響 |
|---|---|
| 棧 | 從棧復(fù)制到堆并被Block持有 |
| 堆 | 被Block持有 |
上面這張表是表達(dá)了什么意思呢? 也就是說(shuō):
- 如果有一個(gè)Block使用某個(gè)__block變量,那么__block變量會(huì)從棧復(fù)制到堆并被Block持有.
- 如果有多個(gè)Block使用某個(gè)__block變量,那么在第一個(gè)Block中__block變量會(huì)從棧復(fù)制到堆并被第一個(gè)Block持有.從第二個(gè)Block時(shí)是持有__block變量,也就是只會(huì)增加__block變量的引用計(jì)數(shù).
對(duì)于__forwarding指針(指向自身的指針),我們?cè)?jīng)說(shuō)過(guò),"不管__block變量配置在棧上還是堆上,都能正確訪問(wèn)該變量."我們可以通過(guò)下面的例子來(lái)說(shuō)明一下情況.
__block int val = 0;
void (^blk)(void) = [^{ ++val; } copy];
++val;
blk();
NSLog(@"%d",val);
通過(guò)blk這個(gè)Block的copy操作, 被__block修飾的val變量成功的從棧上復(fù)制到了堆上了.
所以^{ ++val; }和++val;都可以被轉(zhuǎn)化為以下的形式.
++(val.__forwarding->val);
我們可以通過(guò)下面的示意圖來(lái)表示上面的轉(zhuǎn)變過(guò)程.

截獲對(duì)象的實(shí)現(xiàn)
我們?cè)?jīng)說(shuō)過(guò)截獲變量值,現(xiàn)在我們說(shuō)一下截獲對(duì)象的實(shí)現(xiàn).演示源碼如下所示.
void (^blk)(id obj);
{//array的作用域
id array = [[NSMutableArray alloc] init];
blk = [^(id obj){
[array addObject:obj];
NSLog(@"array count = %ld",[array count]);
} copy];
}//array的作用域已經(jīng)結(jié)束
blk([NSObject new]);
blk([NSObject new]);
blk([NSObject new]);
我們知道array的作用域已經(jīng)結(jié)束了(到達(dá)注釋位置時(shí)候),可以我們調(diào)用block仍然可以訪問(wèn)到array.如下所示,這是為什么呢?

實(shí)際上在blk的實(shí)現(xiàn)過(guò)程中.已經(jīng)持有了array對(duì)象.<<iOS 與OS X多線程和內(nèi)存管理>>是有以下代碼的.
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
id __strong array; //強(qiáng)引用的array成員變量
};
在Objective-C中,C語(yǔ)言結(jié)構(gòu)體并不能含有__strong修飾符的變量.因?yàn)榫幾g器不知道應(yīng)該何時(shí)進(jìn)行C語(yǔ)言結(jié)構(gòu)體的初始化和廢棄操作.不能很好的管理內(nèi)存.Objective-C的運(yùn)行時(shí)庫(kù)可以很好的把握Block從棧上復(fù)制到堆以及堆上的Block被廢棄的時(shí)機(jī).從而有效管理成員變量的持有和釋放.為此,在__main_block_desc_0就增建了兩個(gè)成員變量copy和dispose,已經(jīng)對(duì)應(yīng)的函數(shù).用于成員變量的持有和釋放.如下圖所示.

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}
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};
可是我在實(shí)際過(guò)程中并沒(méi)有__strong修飾詞.個(gè)人猜想是已經(jīng)進(jìn)行了缺省操作了.省略了__strong的修飾符.源碼截圖如下所示.大家可以自行試驗(yàn)操作.

循環(huán)引用的本質(zhì)
上一個(gè)模塊我們說(shuō)了.Block可以持有對(duì)象.如果一個(gè)對(duì)象中含有某個(gè)Block的成員屬性(strong修飾).在Block中直接使用self,會(huì)造成循環(huán)引用,原因就出現(xiàn)__main_block_impl_0結(jié)構(gòu)體中的obj.__main_block_impl_0對(duì)obj是強(qiáng)引用,self對(duì)Block變量是強(qiáng)引用,兩者相互引用,最終造成循環(huán)引用.
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
id __strong obj; //強(qiáng)引用的obj成員變量
};
示意圖如下所示.

結(jié)束
這一篇Block的實(shí)現(xiàn)總共寫了三天,加上自己驗(yàn)證,收獲良多,希望這一篇博客對(duì)大家有所幫助.還是希望大家來(lái)看一下<<iOS 與OS X多線程和內(nèi)存管理>>原書,自己敲一遍實(shí)現(xiàn)源碼,這樣幫助很大,會(huì)加深印象.最后感謝各位看官查看本篇文章.如果有任何問(wèn)題,歡迎聯(lián)系騷棟.歡迎指導(dǎo)批斗.
<<iOS 與OS X多線程和內(nèi)存管理>>的PDF版?zhèn)魉烷T??
