Block本質(zhì)上也是一個OC對象,它內(nèi)部也有個isa指針,它是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象。
閉包 = 一個函數(shù)「或指向函數(shù)的指針」+ 該函數(shù)執(zhí)行的外部上下文變量「也就是自由變量」,Block也是 Objective-C 對于閉包的實現(xiàn)。
一、block的種類
block總共有6種,來源libclosure源碼。
void * _NSConcreteStackBlock[32] = { 0 };
void * _NSConcreteMallocBlock[32] = { 0 };
void * _NSConcreteAutoBlock[32] = { 0 };
void * _NSConcreteFinalizingBlock[32] = { 0 };
void * _NSConcreteGlobalBlock[32] = { 0 };
void * _NSConcreteWeakBlockVariable[32] = { 0 };
我們首先介紹三種常見的block:
void (^block1)(void) = ^{
NSLog(@"-----");
};
int a = 10;
void (^block2)(void) = ^{
NSLog(@"----- %d", a);
};
NSLog(@"%@",block1);
NSLog(@"%@",block2);
NSLog(@"%@",^{
NSLog(@"----- %d", a);
});
打印結(jié)果:
2020-05-06 21:38:29.777677+0800 Block[16006:269295] <__NSGlobalBlock__: 0x101acb090>
2020-05-06 21:38:29.777890+0800 Block[16006:269295] <__NSMallocBlock__: 0x600001aa97a0>
2020-05-06 21:38:29.778054+0800 Block[16006:269295] <__NSStackBlock__: 0x7ffeee1334c8>
1.2 block循環(huán)引用
block的循環(huán)引用處理方式無非weak、strong、__block修飾,不再詳述,這里介紹一種傳參解決方式。
typedef void(^KIBlock)(ViewController *);
@property (nonatomic, copy) KIBlock block;
self.block = ^(ViewController *vc){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
});
};
二、 clang分析
2.1 block的本質(zhì)
先寫一個簡單的block實現(xiàn),然后clang分析一下。
int main(void) {
void (^block)(void) = ^{
NSLog(@"-----");
};
block();
return 0;
}
執(zhí)行clang -rewrite-objc main.m -o main.cpp命令后,整理main.cpp文件:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
//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; //函數(shù)式編程
Desc = desc;
}
};
//block代碼塊內(nèi)部方法實現(xiàn)
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_yp_28c9wg8n09d25v3gszz9j_dstr91j7_T_main_3be554_mi_0);
}
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(void) {
//block的聲明 構(gòu)造函數(shù)
//原始:void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
void (*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
//block調(diào)用實現(xiàn)
//原始:((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
(block->FuncPtr)(block);
return 0;
}
通過clang分析,我們不難發(fā)現(xiàn),block本質(zhì)就是一個結(jié)構(gòu)體,一個對象;也解釋了為什么需要執(zhí)行block()實現(xiàn)調(diào)用。
2.2 值拷貝
聲明一個int變量,并在block內(nèi)打印
int main(void) {
int a = 6;
void (^block)(void) = ^{
NSLog(@"----- %d", a);
};
block();
return 0;
}
clang之后:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//__cself是block結(jié)構(gòu)體即自身,將block拷貝的值賦給新的局部變量a
int a = __cself->a; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_yp_28c9wg8n09d25v3gszz9j_dstr91j7_T_main_a2e2e0_mi_0, a);
}
我們發(fā)現(xiàn)block結(jié)構(gòu)體多了一個屬性a,并通過構(gòu)造函數(shù)將外部變量_a的值賦給a,并在函數(shù)實現(xiàn)時重新生成了一個局部變量a。
所以block內(nèi)部打印的a和外部的a是兩個不同的變量,直接在block內(nèi)部執(zhí)行a++是危險的操作,有引發(fā)變量作用域混淆的風(fēng)險,因此也不被允許。
2.3 指針拷貝
如果想要在block內(nèi)部執(zhí)行a++該怎么操作呢,那么就需要對a執(zhí)行__block修飾。
int main(void) {
__block int a = 6;
void (^block)(void) = ^{
a++;
NSLog(@"----- %d", a);
};
block();
return 0;
}
同樣我們clang分析:
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 void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a)++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_yp_28c9wg8n09d25v3gszz9j_dstr91j7_T_main_540f42_mi_0, (a->__forwarding->a));
}
有沒有發(fā)現(xiàn),多了一個結(jié)構(gòu)體__Block_byref_a_0,下面我們整理一下main方法:
int main(void) {
/**
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
*/
__Block_byref_a_0 a = {
(void*)0,
(__Block_byref_a_0 *)&a,
0,
sizeof(__Block_byref_a_0),
6
};
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
被__block修飾之后的變量a,編譯成了一個結(jié)構(gòu)體__Block_byref_a_0,并保存原始的變量值,然后傳遞一個結(jié)構(gòu)體指針給block。
三、內(nèi)存和簽名
在了解block內(nèi)存和簽名前,我們先從openSource找一份block的開源源碼libclosure-73。
3.1 block數(shù)據(jù)結(jié)構(gòu)
首先我們來看下block的基礎(chǔ)數(shù)據(jù):
struct Block_layout {
void *isa;
volatile int32_t flags; // contains ref count 記錄狀態(tài)的
int32_t reserved;
BlockInvokeFunction invoke;
struct Block_descriptor_1 *descriptor; //
// imported variables
};
關(guān)于flags:
// Values for Block_layout->flags to describe block objects
enum {
//釋放標(biāo)記,一般常用BLOCK_NEEDS_FREE做位與操作,一同傳入Flags,告知該block可釋放
BLOCK_DEALLOCATING = (0x0001), // runtime
//存儲引用計數(shù)的值,是一個可選用參數(shù)
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
BLOCK_NEEDS_FREE = (1 << 24), // runtime
//是否擁有copy、dispose輔助函數(shù)
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler
//是否擁有析構(gòu)函數(shù)
BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code
BLOCK_IS_GC = (1 << 27), // runtime
//是否是全局block
BLOCK_IS_GLOBAL = (1 << 28), // compiler
BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
//與BLOCK_USE_STRET相對,判斷是否當(dāng)前block擁有一個簽名。
BLOCK_HAS_SIGNATURE = (1 << 30), // compiler
BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
};
關(guān)于簽名:
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size;
};
// 可選
#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
// requires BLOCK_HAS_COPY_DISPOSE
BlockCopyFunction copy;
BlockDisposeFunction dispose;
};
// 可選
#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
// requires BLOCK_HAS_SIGNATURE
const char *signature;
const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
3.2 lldb調(diào)試
int a = 6;
void(^block)(void) = ^ {
NSLog(@"--- %d", a);
};
block();
斷點進(jìn)入objc_retainBlock,然后讀取x0寄存器「必須真機(jī)」register read x0:


可以看到此時為__ NSStackBlock__即棧block,繼續(xù)執(zhí)行,會進(jìn)入
_Block_copy,在最后一行return代碼處打斷點:

此時為__ NSMallocBlock__。
3.3 簽名
block是匿名函數(shù),那么作為一個函數(shù),block肯定也有自己的簽名,上面lldb調(diào)試打印的結(jié)果已經(jīng)有體現(xiàn)。
我們在講述block的源碼中提到block有兩個可選的參數(shù)Block_descriptor_2和Block_descriptor_3。而block的簽名信息就放在Block_descriptor_3中,一個名為signature的元素。
從上面的打印結(jié)果找到簽名:
signature: "v8@?0"
其中
v表示返回值是void,@?表示未知的對象,即為block。這和方法簽名是有所不同的,方法簽名一般是v@:這樣的形式(此處只說返回值為void的場景),:表示SEL。
四、StackBlock到MallocBlock的copy實現(xiàn)
在上面LLDB調(diào)試的時候,我們看到了一個函數(shù)_Block_copy,下面我們看下具體實現(xiàn),代碼已經(jīng)加了注釋。
// Copy, or bump refcount, of a block. If really copying, call the copy helper if present.
// 棧 -> 堆 研究拷貝
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
//引用計數(shù)處理,沒有走truntime下層,自己處理
//內(nèi)部實現(xiàn)為什么是+2:因為”BLOCK_DEALLOCATING = (0x0001)“,0x0001這個位置已經(jīng)被占用
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
else {
// Its a stack block. Make a copy.
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size);
if (!result) return NULL;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;
#endif
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
result->isa = _NSConcreteMallocBlock;
return result;
}
}
五、__blcok原理
我們都知道:Block不允許修改外部變量的值,這里所說的外部變量的值,指的是棧中指針的內(nèi)存地址。
__block 所起到的作用就是只要觀察到該變量被 block 所持有,就將“外部變量”在棧中的內(nèi)存地址放到了堆中,進(jìn)而在block內(nèi)部也可以修改外部變量的值。
下面我們打印指針地址驗證一下:
__block int a = 0;
NSLog(@"定義前:%p", &a); //棧區(qū)
void (^blcok)(void) = ^{
a = 1;
NSLog(@"block內(nèi)部:%p", &a); //堆區(qū)
};
NSLog(@"定義后:%p", &a); //堆區(qū)
blcok();
2020-05-09 12:07:37.350556+0800 block[8223:91646] 定義前:0x7ffee5d78cf0
2020-05-09 12:07:37.351701+0800 block[8223:91646] 定義后:0x60000011df58
2020-05-09 12:07:37.351863+0800 block[8223:91646] block內(nèi)部:0x60000011df58
0x7:棧地址;0x6:堆地址;0x1:全局區(qū)。
下面我們通過源碼分析,先來一段簡單的block代碼:
#import <Foundation/Foundation.h>
int main(void) {
__block NSString *name = [NSString stringWithString:@"name"];
void(^block)(void) = ^ {
NSLog(@"--- %@", name);
};
block();
return 1;
}
clang編譯處理后的代碼:
struct __Block_byref_name_0 {
void *__isa;
__Block_byref_name_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSString *name;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_name_0 *name; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_name_0 *_name, int flags=0) : name(_name->__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_name_0 *name = __cself->name; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_yp_28c9wg8n09d25v3gszz9j_dstr91j7_T_main_3b3b70_mi_1, (name->__forwarding->name));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->name, (void*)src->name, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->name, 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(void) {
//__attribute__((__blocks__(byref))) __Block_byref_name_0 name = {(void*)0,(__Block_byref_name_0 *)&name, 33554432, sizeof(__Block_byref_name_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithString:"), (NSString *)&__NSConstantStringImpl__var_folders_yp_28c9wg8n09d25v3gszz9j_dstr91j7_T_main_3b3b70_mi_0)};
__Block_byref_name_0 name = {
(void*)0,
(__Block_byref_name_0 *)&name,
33554432,
sizeof(__Block_byref_name_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithString:"), (NSString *)&__NSConstantStringImpl__var_folders_yp_28c9wg8n09d25v3gszz9j_dstr91j7_T_main_3b3b70_mi_0)
};
//void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_name_0 *)&name, 570425344));
void(*block)(void) = &__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA,
(__Block_byref_name_0 *)&name,
570425344);
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 1;
}
編譯后,在main中,主要多了兩個方法:__Block_byref_id_object_copy_131、__Block_byref_id_object_dispose_131,一個結(jié)構(gòu)體:&__main_block_desc_0_DATA。
5.1 __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);
}
這里調(diào)用的是_Block_object_assign(下面具體分析),方法內(nèi)部目標(biāo)對象偏移了40字節(jié),由于此時目標(biāo)對象就是__Block_byref_name_0,偏移40字節(jié)正好是NSString *name。也就是說此時對name對象的內(nèi)存地址做了一次copy。
struct __Block_byref_name_0 {
void *__isa; // 8
__Block_byref_name_0 *__forwarding; // 8
int __flags; // 4
int __size; // 4
void (*__Block_byref_id_object_copy)(void*, void*); // 8
void (*__Block_byref_id_object_dispose)(void*); // 8
NSString *name;
};
5.2 &__main_block_desc_0_DATA
block的構(gòu)造函數(shù)傳進(jìn)來一個結(jié)構(gòu)體&__main_block_desc_0_DATA,該結(jié)構(gòu)體在初始化的時候傳入了__main_block_copy_0方法進(jìn)行拷貝操作,它里面只有一個方法_Block_object_assign,實現(xiàn)如下(runtime.cpp文件):
// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
// see function implementation for a more complete description of these fields and combinations
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ... 截獲的是對象
BLOCK_FIELD_IS_BLOCK = 7, // a block variable 截獲的是block變量
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable 截獲的是__block修飾的對象
BLOCK_FIELD_IS_WEAK = 16, // declared __weak, only used in byref copy helpers 截獲的是__weak修飾的對象
BLOCK_BYREF_CALLER = 128, // called from __block (byref) copy/dispose support routines.
};
//
// When Blocks or Block_byrefs hold objects(自動捕獲到變量) then their copy routine helpers use this entry point
// to do the assignment.
void _Block_object_assign(void *destArg, const void *object, const int flags) {
const void **dest = (const void **)destArg;
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
case BLOCK_FIELD_IS_OBJECT:
/*******
id object = ...;
[^{ object; } copy];
********/
//截獲的變量是對象,只需要賦值,引用計數(shù)不做任何處理,因為對象的引用計數(shù)是runtime底層自己處理的。
_Block_retain_object(object);
*dest = object;
break;
case BLOCK_FIELD_IS_BLOCK:
/*******
void (^object)(void) = ...;
[^{ object; } copy];
********/
//如果截獲的變量是block對象,調(diào)用_Block_copy方法。
*dest = _Block_copy(object);
break;
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
/*******
// copy the onstack __block container to the heap
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__block ... x;
__weak __block ... x;
[^{ x; } copy];
********/
*dest = _Block_byref_copy(object);
break;
........
}
如果變量是__block修飾的對象,調(diào)用_Block_byref_copy方法。
它會重新申請一塊堆內(nèi)存,然后將截獲的對象也就是上述例子中的__Block_byref_name_0結(jié)構(gòu)體賦值給新的結(jié)構(gòu)體,并將copy的對象和源對象的forwarding指針都指向新生成的結(jié)構(gòu)體。其實也就是對__block修飾的對象做了一次拷貝動作,然后讓他們都指向同一塊內(nèi)存區(qū)域達(dá)到修改其中一個兩個都改變的目的。
static struct Block_byref *_Block_byref_copy(const void *arg) {
// 創(chuàng)建一個臨時變量
struct Block_byref *src = (struct Block_byref *)arg;
if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// src points to stack
// 1.申請堆內(nèi)存空間
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
// 2. 給新申請的空間賦值
copy->isa = NULL;
// byref value 4 is logical refcount of 2: one for caller, one for stack
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
// copy的對象和源對象都指向堆內(nèi)存的拷貝地址
copy->forwarding = copy; // patch heap copy to point to itself 堆拷貝指向自己
src->forwarding = copy; // patch stack to point to heap copy 棧指向堆拷貝
copy->size = src->size;
if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
// 處理desc2 內(nèi)存偏移取值
struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
copy2->byref_keep = src2->byref_keep;
copy2->byref_destroy = src2->byref_destroy;
if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
// 處理desc3
struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
copy3->layout = src3->layout;
}
(*src2->byref_keep)(copy, src);
} else {
memmove(copy+1, src+1, src->size - sizeof(*src));
}
} else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
return src->forwarding;
}
copy最終的代碼調(diào)用了一個方法byref_keep,那么這個方法是干什么的呢?
我們點擊查看這個方法,發(fā)現(xiàn)他是Block_byref結(jié)構(gòu)體的第五個參數(shù),我們重新回到main.cpp文件找到__Block_byref_name_0結(jié)構(gòu)體,在main方法初始化的時候,第五個參數(shù)正好對應(yīng)__Block_byref_id_object_copy_131,即 byref_keep = __Block_byref_id_object_copy_131,完成對block修飾變量的內(nèi)存拷貝,這個方法在前面已經(jīng)介紹。
由上,我們可以得出結(jié)論:__block修飾的外部變量,在block內(nèi)部可以修改,是因為發(fā)生了3次拷貝:
-
_Block_copy,block的拷貝,從棧內(nèi)存到堆內(nèi)存。 -
__main_block_copy_0,對新生成的結(jié)構(gòu)體的拷貝。__block修飾的變量會生成一個名為__Block_byref_xxx_0結(jié)構(gòu)體,將原來的進(jìn)行了封裝,然后把整個結(jié)構(gòu)體地址指針傳入block內(nèi)部。(即,在block內(nèi)部,把block外部的__Block_byref_xxx_0結(jié)構(gòu)體,copy一份新的,然后block內(nèi)外的結(jié)構(gòu)體forwarding指針都指向新copy的結(jié)構(gòu)體,保持block內(nèi)外一致) -
__Block_byref_id_object_copy_131,對原來的(__block修飾)對象的內(nèi)存的拷貝,存入第二步copy的結(jié)構(gòu)體。
參考文檔:
蘋果官方文檔 Blocks and Variables
底層源碼 libclosure-73