前言
iOS-深入研究Block這篇文章結(jié)合實(shí)例介紹了Block的類型,循環(huán)引用等問題,接著我們通過Block的源碼分析一下,它的底層是怎么操作的?
1 通過Clang分析Block
Block通過Clang將會(huì)編譯成什么樣的結(jié)構(gòu)呢,它的invoke,isa,簽名的原理是什么,我們來研究下。
#include "stdio.h"
int main(){
// __Block_byref_a_0 int a = 18;
int a = 8;
void(^block)(void) = ^{
// a++;
printf("ro_robert - %d",a);
};
block();
return 0;
}
我們通過xcrun -sdk iphonesimulator clang -S -rewrite-objc -fobjc-arc -fobjc-runtime=ios-14.5 block.c命令執(zhí)行一個(gè)得到block.cpp文件.
我們打開block.cpp文件,找到main函數(shù),如下
int main(){
int a = 8;
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
從上可以看出,我們要研究的 void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));*是這行代碼。
我們經(jīng)過調(diào)整下,如下
void(*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a));
void(*block)(void) 這是一個(gè)函數(shù)指針,
這里的__main_block_impl_0就是一個(gè)函數(shù)調(diào)用。
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
把這行也調(diào)下,如下所示
block->FuncPtr(block);
我們先看下__main_block_impl_0這個(gè)是什么,經(jīng)過搜下,如下
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;
}
};
這是一個(gè)結(jié)構(gòu)體,
void(*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a));
就是這個(gè)結(jié)構(gòu)體的構(gòu)造函數(shù),傳了三個(gè)參數(shù),其中有一個(gè)我們傳的參數(shù)是a,這個(gè)__main_block_impl_0這個(gè)結(jié)構(gòu)體中也有一個(gè)變量a,他們有什么關(guān)系呢,我們來看下。
我們?cè)赽lock.c中把這個(gè)變量a去掉,看下是什么效果。
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;
}
};
這個(gè)就是現(xiàn)在__main_block_impl_0結(jié)構(gòu)體,我們發(fā)現(xiàn)這里的沒有a這個(gè)變量了,它的構(gòu)造函數(shù)同樣也沒有a這個(gè)參數(shù)。
我們?cè)賮砜聪轮暗慕Y(jié)構(gòu)體的構(gòu)造函數(shù)
__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;
}
這里: a(_a) 是C++語法,默認(rèn)會(huì)對(duì)傳過來的參數(shù)a賦值,_a會(huì)傳給a,說白了就是賦值操作,block在底層會(huì)把變量捕獲進(jìn)來,變成自己的成員變量。
我們通過真實(shí)的App代碼,如下
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *objc = [NSObject alloc];
__block NSObject *objc1 = [NSObject alloc];
void (^block1)(void) = ^{
NSLog(@"ro_Block %@ ",objc1);
};
block1();
}
我們打開ViewController.cpp文件,找到__ViewController__viewDidLoad_block_impl_0這個(gè)結(jié)構(gòu)體,如下
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
NSObject *__strong objc1;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, NSObject *__strong _objc1, int flags=0) : objc1(_objc1) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
這里面有一個(gè)NSObject *__strong objc1;對(duì)象,這說明我們的Block在捕獲取變量時(shí)會(huì)生成相應(yīng)的成員變量。
在編譯價(jià)段,impl.isa = &_NSConcreteStackBlock;這里是一個(gè)stackBlock;。
之前我們介紹過,捕獲局部或者屬性變量,又不是弱引用,應(yīng)該是這個(gè)MallocBlock,為什么這里是StatckBlock,我們來看下。
在這里我們是在編譯時(shí)是StackBlock,程序還要經(jīng)過運(yùn)行時(shí),才會(huì)變成堆,是如何變成堆呢,需要我們?cè)傺芯肯隆?br> 這里有一個(gè)fp參數(shù),是第一個(gè)參數(shù),經(jīng)過查找,它是__ViewController__viewDidLoad_block_func_0,而它又是這個(gè)
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
NSObject *__strong objc1 = __cself->objc1; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_r7_rnm6hs1x2jg8bqy7sjwsskx00000gn_T_ViewController_2b1512_mi_0,objc1);
}
函數(shù),就是傳一個(gè)函數(shù)過去,之后進(jìn)行了FuncPtr的執(zhí)行,就是去執(zhí)行__ViewController__viewDidLoad_block_func_0這個(gè)函數(shù),就是運(yùn)行起來。
block函數(shù)式保存,如果不調(diào)用執(zhí)行,永遠(yuǎn)也沒可能執(zhí)行它的功能邏輯。
我們切換到block.c代碼中,為了防止干擾。
#include "stdio.h"
int main(){
// __Block_byref_a_0 int a = 18;
int a = 18;
void(^block)(void) = ^{
// a++;
printf("ro_robert - %d",a);
// printf("ro_robert");
};
block();
return 0;
}
通過命令編成cpp,我們看下__main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
printf("ro_robert - %d",a);
}
這里csef就是傳過來的就是自己,
int a = __cself->a;這里的int a 與 __cself->a,就是賦值操作,內(nèi)容相同,但是地址不同,說白就是值拷貝。
接下來,我們把int a加上__block看下效果,代碼如下
#include "stdio.h"
int main(){
// __Block_byref_a_0 int a = 18;
__block int a = 18;
void(^block)(void) = ^{
a++;
printf("ro_robert - %d",a);
// printf("ro_robert");
};
block();
return 0;
}
再次編譯,我們?cè)俜治鱿耞_block做了什么操作?
我們看下__main_block_impl_0的結(jié)構(gòu)體
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;
}
};
我們發(fā)現(xiàn)int a 變成 *__Block_byref_a_0 a;
我們?cè)傧耺ain函數(shù)
int main(){
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 18};
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;
}
經(jīng)過整理,如下
int main(){
__Block_byref_a_0 a = {(void*)0,
(__Block_byref_a_0 *)&a,
0,
sizeof(__Block_byref_a_0),
18};
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_byref_a_0結(jié)構(gòu)體初始化,* (__Block_byref_a_0 )&a,這里取a的地址。
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
這里是__Block_byref_a_0這個(gè)結(jié)構(gòu)體,__forwarding這里指向a的地址,在這個(gè)__main_block_impl_0結(jié)構(gòu)體中把__forwarding傳給了參數(shù)a,在__main_block_func_0這個(gè)函數(shù)中
__Block_byref_a_0 *a = __cself->a;
這里的__cself->a;與外界的a是一樣的,指向同一塊內(nèi)存區(qū)域,說明的就是指針拷貝。
__block生成的__Block_byref_a_0這樣的結(jié)構(gòu)體,傳給的block的是指針地址,這就是為什么加了__block可以修改外部變量。
2 Block的簽名和copy過程
我們?cè)赽lock.cpp文件發(fā)現(xiàn)__main_block_copy_0,__main_block_dispose_0,__main_block_desc_0這些結(jié)構(gòu)體,他們是干什么用的呢,我們來分析下block的copy過程。
我們通過斷點(diǎn),分析下匯編流程,調(diào)試代碼如下
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *objc = [NSObject alloc];
NSObject *objc1 = [NSObject alloc];
void (^block1)(void) = ^{
NSLog(@"robert_Block %@ ",objc1);
};
block1();
}
我們?cè)赽lock打斷點(diǎn),調(diào)試,如下

這里調(diào)用了objc_retainBlock函數(shù),走進(jìn)這個(gè)函數(shù)(也可以通過符號(hào)斷點(diǎn)),發(fā)現(xiàn)調(diào)用了_Block_copy這個(gè)函數(shù),通過符號(hào)斷點(diǎn),是在libsystem_blocks.dylib中,這個(gè)是沒有開源的,我們找替找工程libclosure(這里用79版本),可以查看到,我們看下它的源碼,如下
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
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
else {// 棧 - 堆 (編譯期)
// Its a stack block. Make a copy.
size_t size = Block_size(aBlock);
struct Block_layout *result = (struct Block_layout *)malloc(size);
if (!result) return NULL;
memmove(result, aBlock, size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;
#if __has_feature(ptrauth_signed_block_descriptors)
if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
uintptr_t oldDesc = ptrauth_blend_discriminator(
&aBlock->descriptor,
_Block_descriptor_ptrauth_discriminator);
uintptr_t newDesc = ptrauth_blend_discriminator(
&result->descriptor,
_Block_descriptor_ptrauth_discriminator);
result->descriptor =
ptrauth_auth_and_resign(aBlock->descriptor,
ptrauth_key_asda, oldDesc,
ptrauth_key_asda, newDesc);
}
#endif
#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;
}
}
我們知道Block是一個(gè)結(jié)構(gòu)體,struct Block_layout *aBlock;是Block_layout類型的,它的源碼如下
struct Block_layout {
void * __ptrauth_objc_isa_pointer isa;
volatile int32_t flags; // contains ref count
int32_t reserved;
BlockInvokeFunction invoke;
struct Block_descriptor_1 *descriptor;
// imported variables
};
- 這里面有isa指針
- flags標(biāo)識(shí)
- invoke調(diào)用函數(shù)
- descriptor其它相關(guān)描述,是否正在析構(gòu)等。
這個(gè)flags標(biāo)識(shí)符的定義有
// Values for Block_layout->flags to describe block objects
enum {
BLOCK_DEALLOCATING = (0x0001), // runtime 正在析構(gòu)
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime 掩碼
BLOCK_INLINE_LAYOUT_STRING = (1 << 21), // compiler
#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
BLOCK_SMALL_DESCRIPTOR = (1 << 22), // compiler
#endif
BLOCK_IS_NOESCAPE = (1 << 23), // compiler
BLOCK_NEEDS_FREE = (1 << 24), // runtime
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler
BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code
BLOCK_IS_GC = (1 << 27), // runtime
BLOCK_IS_GLOBAL = (1 << 28), // compiler
BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30), // compiler 簽名
BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
};
我們對(duì)_Block_copy符號(hào)斷點(diǎn),然后通命令查看,如圖

這是一個(gè)全局block。我們?cè)俑南麓a,讓這個(gè)block捕獲外部變量,如圖

這是一個(gè)stackBlock,這個(gè)block應(yīng)該是MallocBlock,因?yàn)檫@里還沒有經(jīng)過copy操作,當(dāng)我們執(zhí)行完copy操作后,如圖

這里成為了MallocBlock,我們?cè)倏瓷厦鎋Block_copy的源碼,
*aBlock = (struct Block_layout *)arg;*
這里轉(zhuǎn)換Block_layout類型。
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
這是是引用計(jì)數(shù)據(jù)相關(guān)處理。
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
如果是GlobalBlock直接返回。
經(jīng)過編譯期過來的是不能生成堆的,當(dāng)發(fā)現(xiàn)是一個(gè)stackBlock,又捕獲了外界變量,
size_t size = Block_size(aBlock);
struct Block_layout *result = (struct Block_layout *)malloc(size);
這里就會(huì)根據(jù)原來的大小,就會(huì)開辟一段內(nèi)存空間,然后把原始的數(shù)據(jù)拷貝到新的Block_layout中,isa指針標(biāo)記為_NSConcreteMallocBlock,這里就變成了堆Block。
上圖中所示的invoke是函數(shù)調(diào)用者,signatrue就是Block的簽名。
signatrue的解釋 v8@?0 v代表返回值,8代表8字節(jié), @?代表Block類型,0代表從0號(hào)位置開始
3 Blocklayout的結(jié)構(gòu)
當(dāng)我們invoke的時(shí)候,這個(gè)消息會(huì)失效或有問題,會(huì)進(jìn)入消息轉(zhuǎn)發(fā)流程,在最后慢速轉(zhuǎn)發(fā)流程時(shí),必須要獲取簽名才能進(jìn)行invocation.
我再次看下blocklayout的結(jié)構(gòu)體
struct Block_layout {
void * __ptrauth_objc_isa_pointer isa;
volatile int32_t flags; // contains ref count
int32_t reserved;
BlockInvokeFunction invoke;
struct Block_descriptor_1 *descriptor;
// imported variables
};
我們看下descriptor這個(gè)類型,如下
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size; // 大小
};
descriptor是可選參數(shù),內(nèi)存連續(xù)可選,因?yàn)槲覀兊念愋筒灰粯樱越Y(jié)構(gòu)也不一樣,因?yàn)橛衧tackBlock,MallocBlock,GlobalBlock的區(qū)分,每個(gè)類型的結(jié)構(gòu)體不一樣,通過標(biāo)識(shí)符判斷。
我們?cè)谠创a找到
#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
};
這些結(jié)構(gòu)體,這些都是通過標(biāo)識(shí)符號(hào)來判斷。
static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
uint8_t *desc = (uint8_t *)_Block_get_descriptor(aBlock);
desc += sizeof(struct Block_descriptor_1);
return (struct Block_descriptor_2 *)desc;
}
這里通過內(nèi)存平移來獲取到Block_descriptor_2,我們分析下。
static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
uint8_t *desc = (uint8_t *)_Block_get_descriptor(aBlock);
desc += sizeof(struct Block_descriptor_1);
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
desc += sizeof(struct Block_descriptor_2);
}
return (struct Block_descriptor_3 *)desc;
}
這里也是通過平移,判斷有沒有Block_descriptor_2,有的話,再加上Block_descriptor_2的大小就是Block_descriptor_3的起始位置。
我們看下Block_descriptor_3的結(jié)構(gòu),如
struct Block_descriptor_3 {
// requires BLOCK_HAS_SIGNATURE
const char *signature;
const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
這里有signature,只要有signature的打印,就說明肯定有Block_descriptor_3,我們可以通過x/6gx命令,查看內(nèi)存。
4 Block的捕獲變量生命周期
Block捕獲變量時(shí)都做了哪些操作,_Block_copy這個(gè)函數(shù)做了什么?帶著這些疑問,我們繼續(xù)分析底層原理。
我們先看下ViewController.cpp文件,在文件中搜索__ViewController__viewDidLoad_block_desc_0,這個(gè)函數(shù),源碼如下
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0), __ViewController__viewDidLoad_block_copy_0, __ViewController__viewDidLoad_block_dispose_0};
這里copy和dispose兩個(gè)函數(shù),__ViewController__viewDidLoad_block_dispose_0這個(gè)就是 Block_descriptor_2中的dispose,__ViewController__viewDidLoad_block_copy_0是_Block_copy函數(shù)的指針,它做了什么事情呢,我們來看下,__ViewController__viewDidLoad_block_copy_0這個(gè)函數(shù)里面會(huì)調(diào)用_Block_object_assign這個(gè)函數(shù),相當(dāng)于copy函數(shù)。
我們繼續(xù)分析_Block_object_assign,看看它的操作流程, 我們?cè)谠创a中搜下_Block_object_assign*這個(gè)函數(shù),如下
The flags parameter of _Block_object_assign and _Block_object_dispose is set to
* BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object, // 普通object對(duì)象,未使用__block修飾的
* BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
* BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.對(duì)應(yīng)__block修飾
If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16)
這里是三個(gè)標(biāo)識(shí)符號(hào),捕獲變量的判斷和相應(yīng)處理。我們看下_Block_object_assign函數(shù)的源碼,如
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];
********/
// _Block_retain_object_default = fn (arc)
_Block_retain_object(object);
*dest = object;
break;
case BLOCK_FIELD_IS_BLOCK:
/*******
void (^object)(void) = ...;
[^{ object; } 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;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
/*******
// copy the actual field held in the __block container
// Note this is MRC unretained __block only.
// ARC retained __block is handled by the copy helper directly.
__block id object;
__block void (^object)(void);
[^{ object; } copy];
********/
*dest = object;
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
/*******
// copy the actual field held in the __block container
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__weak __block id object;
__weak __block void (^object)(void);
[^{ object; } copy];
********/
*dest = object;
break;
default:
break;
}
}
如果是BLOCK_FIELD_IS_OBJECT普通對(duì)象類型,執(zhí)行
_Block_retain_object(object);
*dest = object;
_Block_retain_object調(diào)用了_Block_retain_object_default,這個(gè)函數(shù)空實(shí)現(xiàn),默認(rèn)交給系統(tǒng)級(jí)別的ARC操作。
對(duì)象賬值給目標(biāo)dest,具備相同的內(nèi)存空間。
BLOCK_FIELD_IS_BLOCK Block類型,執(zhí)行
*dest = _Block_copy(object);
_Block_copy調(diào)用。
BLOCK_FIELD_IS_BYREF類型,執(zhí)行
*dest = _Block_byref_copy(object);
_Block_byref_copy這個(gè)函數(shù)。我們看下它的源碼,如下
static struct Block_byref *_Block_byref_copy(const void *arg) {
struct Block_byref *src = (struct Block_byref *)arg;
//
if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// src points to stack
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
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->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) {
// Trust copy helper to copy everything of interest
// If more than one field shows up in a byref block this is wrong XXX
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) {
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;
}
// 捕獲到了外界的變量 - 內(nèi)存處理 - 生命周期的保存
(*src2->byref_keep)(copy, src);
}
else {
// Bitwise copy.
// This copy includes Block_byref_3, if any.
memmove(copy+1, src+1, src->size - sizeof(*src));
}
}
// already copied to heap
else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
return src->forwarding;
}
- if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) 引用計(jì)數(shù)相關(guān)的處理,
arc自己找。
struct Block_byref *copy = (struct Block_byref )malloc(src->size);這里copy一份
copy->isa = NULL;為空。
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4; 標(biāo)識(shí)符賦值
copy->forwarding = copy; forwarding拷貝一份
src->forwarding = copy;copy賦給原始的forwarding,這說明原來的forwarding和copy后的forwarding是同一個(gè),都是棧到堆,forwarding傳給了objc1這個(gè)對(duì)象。
if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE)這里判斷BLOCK_BYREF_HAS_COPY_DISPOSE*被捕獲的變量進(jìn)到這個(gè)判斷里面。
接著執(zhí)行
copy2->byref_keep = src2->byref_keep;
copy2->byref_destroy = src2->byref_destroy;
原始對(duì)象與copy對(duì)象一樣。
(*src2->byref_keep)(copy, src);
這里byref_keep進(jìn)行調(diào)用,我們捕獲取外界的變量,對(duì)它進(jìn)行相關(guān)內(nèi)存的處理,賦值操作,我們看下byref_keep源碼
struct Block_byref_2 {
// requires BLOCK_BYREF_HAS_COPY_DISPOSE
BlockByrefKeepFunction byref_keep;
BlockByrefDestroyFunction byref_destroy;
};
對(duì)外界變量生命周期的保存,如果外界的變量變?yōu)閚il了,block內(nèi)部的變量也成為nil了。
byref_keep相當(dāng)于copy函數(shù),
byref_destroy相當(dāng)于dipose函數(shù)
最后調(diào)用的就是_Block_object_assign這個(gè)函數(shù),在ViewController.cpp中搜下這個(gè)函數(shù),找到如下代碼
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
dst + 40就是copy,我們內(nèi)部的變量,也是Block_byref類型,占用24字節(jié),Block_byref_2占用16字節(jié),剛好這里就是Block_byref_3這個(gè)結(jié)構(gòu)體。
struct Block_byref_3 {
// requires BLOCK_BYREF_LAYOUT_EXTENDED
const char *layout;
};
這是Block_byref_3的結(jié)構(gòu)體,這里相當(dāng)于傳的是object1這個(gè)對(duì)象。
__block修飾的變量流程
- copy,從??截惖綏I?/strong>
- block捕獲變量 _Block_byref類型
- 對(duì)_Block_byref對(duì)象進(jìn)行一次copy操作
- 針對(duì)_Block_byref里面修飾的object進(jìn)行copy操作
- byref_keep調(diào)用這個(gè)函數(shù)把里面的對(duì)象保存一下
- 釋放調(diào)用_Block_object_dispose這個(gè)函數(shù)
void _Block_object_dispose(const void *object, const int flags) {
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
// get rid of the __block data structure held in a Block
_Block_byref_release(object);
break;
case BLOCK_FIELD_IS_BLOCK:
_Block_release(object);
break;
case BLOCK_FIELD_IS_OBJECT:
_Block_release_object(object);
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
break;
default:
break;
}
}
這里_Block_object_dispose函數(shù)的源碼
- BLOCK_FIELD_IS_OBJECT普通對(duì)象,執(zhí)行
_Block_release_object(object);
調(diào)用_Block_release_object_default這個(gè)函數(shù),空實(shí)現(xiàn)
- BLOCK_FIELD_IS_BLOCK如果是Block類型,執(zhí)行
_Block_release(object);
_Block_release的源碼
void _Block_release(const void *arg) {
struct Block_layout *aBlock = (struct Block_layout *)arg;
if (!aBlock) return;
if (aBlock->flags & BLOCK_IS_GLOBAL) return;
if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;
if (latching_decr_int_should_deallocate(&aBlock->flags)) {
_Block_call_dispose_helper(aBlock);
_Block_destructInstance(aBlock);
free(aBlock);
}
}
_Block_destructInstance銷毀實(shí)例對(duì)象, free(aBlock);釋放block。
- BLOCK_FIELD_IS_BYREF byref類型(被_block捕獲的類型),執(zhí)行
_Block_byref_release(object);
_Block_byref_release的源碼
static void _Block_byref_release(const void *arg) {
struct Block_byref *byref = (struct Block_byref *)arg;
// dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
byref = byref->forwarding;
if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
os_assert(refcount);
if (latching_decr_int_should_deallocate(&byref->flags)) {
if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref+1);
(*byref2->byref_destroy)(byref);
}
free(byref);
}
}
}
這里調(diào)用了byref_destroy這個(gè)函數(shù),free(byref);釋放byref這個(gè)變量,byref里面的對(duì)象也會(huì)釋放。
else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}如果不是正常對(duì)象 latching_incr_int執(zhí)行這個(gè)函數(shù),自己處理。
總結(jié)
這篇文章我們通過源碼分析了Block的底層是如何工作的,它的copy流程,釋放流程,捕獲對(duì)象做了比較詳細(xì)的分析。通過這篇文章我們對(duì)Block的底層有了很深層次的認(rèn)識(shí),有疑問,歡迎大家隨時(shí)來交流學(xué)習(xí)。