前言
上篇文章我们介绍了block的内存管理,block进行copy操作,会引起auto变量的引用计数发生变化,那么block的copy底层是什么实现的?本篇从blocks官方源码说一下block的copy实现
官方源码 libclousure-79下载地址
copy的层级
首选介绍下 copy层级
case 1 // 1级copy struct __main_block_impl_0 需要copy
示例代码
int age = 6;
void(^block)(void) = ^{
NSLog(@"a==%d",age);
};
case 2 // 2级copy struct __main_block_impl_0 需要copy NSObject *obj需要copy
示例代码
NSObject *obj = [NSObject new];
void(^block)(void) = ^{
NSLog(@"a==%@",obj);
};
case 3 // 2级copy struct __main_block_impl_0 需要copy struct __Block_byref_age_0 需要copy
示例代码
__block int age = 6;
void(^block)(void) = ^{
NSLog(@"a==%d",age);
};
case 4 // 3级copy struct __main_block_impl_0 需要copy struct __Block_byref_obj_0 需要copy NSObject *obj需要copy
示例代码
__block NSObject *obj = [NSObject new];
void(^block)(void) = ^{
NSLog(@"a==%@",obj);
};
分析
- auto类型为基本数据类型 struct __main_block_impl_0需要copy
- auto类型为对象类型 struct __main_block_impl_0 需要copy NSObject *obj需要copy
- __block修饰auto类型为基本数据类型 struct __main_block_impl_0需要copy struct __Block_byref_age_0需要copy
- __block修饰auto类型为对象类型 struct __main_block_impl_0需要copy struct __Block_byref_obj_0需要copy NSObject *obj需要copy
核心函数
通常我们对Block的copy实际是调用了NSBlock的copy方法,而它的copy方法也很简单,直接调用了libsystem_blocks.dylib中的_Block_copy,libsystem_blocks.dylib是专门来处理block的框架,代码是开源的。
blocks库提供的几个核心函数都在 runtime.cpp 文件中
// block结构体自身的copy
_Block_copy
// 为block的内部成员变量寻找copy函数
_Block_call_copy_helper
// 区分copy函数的参数类型
_Block_object_assign
// __block变量copy
_Block_byref_copy
// oc对象类型的copy
_Block_retain_object
block copy的起点
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_call_copy_helper,然后 _Block_call_copy_helper 又调用了很多层,来回绕。
为了流程清晰,我们按照示例只留下相关代码
例子一 auto类型为基本数据类型
示例代码
int age = 6;
void(^block)(void) = ^{
NSLog(@"a==%d",age);
};
简化后的copy流程
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
// 1、强转成真正的block结构 Block_layout类型
aBlock = (struct Block_layout *)arg;
size_t size = Block_size(aBlock);
// 2、生成一个堆block
struct Block_layout *result = (struct Block_layout *)malloc(size);
if (!result) return NULL;
// 3、栈block内容 完全拷贝到堆上
memmove(result, aBlock, size);
result->invoke = aBlock->invoke;
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);
// 4、设置flags为2
result->flags |= BLOCK_NEEDS_FREE | 2;
// 5、设置isa 为堆block
result->isa = _NSConcreteMallocBlock;
return result;
}
分析
- 强转成真正的block结构 Block_layout类型 为什么可以强转 看我这篇文章
- 生成一个堆block
- 栈block内容 完全拷贝到堆上
- 设置flags为2
- 设置isa为堆block
copy流程结束
简化过程
基本类型变量进入 _Block_call_copy_helper 函数之后 直接return
例子二 auto类型是对象类型
示例代码
NSObject *obj = [NSObject new];
void(^block)(void) = ^{
NSLog(@"a==%@",obj);
};
观察Clang下的c++代码
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
// 问题1 copy函数什么时候调用
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};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// 问题2 NSObject *obj的copy流程
NSObject *obj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_obj, int flags=0) : obj(_obj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSObject *obj = __cself->obj; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_zm_558cwfjs099fbm2r8kxg8wt00000gt_T_main_c7596b_mi_0,obj);
}
// 问题3 _Block_object_assign第3个参数 3/*BLOCK_FIELD_IS_OBJECT*/ 是做什么用的
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
/* @autoreleasepool */ {
__AtAutoreleasePool __autoreleasepool;
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"));
// 问题4 block Flags 设置成570425344 代表什么
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
}
return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}
问题汇总:
1、__main_block_desc_0生成的copy函数什么时候调用?
2、__main_block_impl_0结构体中NSObject *obj的copy流程是怎样的?
3、_Block_object_assign第3个参数 3/BLOCK_FIELD_IS_OBJECT/ 是做什么用的?
4、__block_impl的Flags参数 设置成570425344 代表什么?
我们带着问题看copy流程
简化后的copy流程
Step1: copy struct __main_block_impl_0
// 第一层 copy block copy
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
aBlock = (struct Block_layout *)arg;
size_t size = Block_size(aBlock);
struct Block_layout *result = (struct Block_layout *)malloc(size);
if (!result) return NULL;
memmove(result, aBlock, size);
result->invoke = aBlock->invoke;
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);
result->flags |= BLOCK_NEEDS_FREE | 2;
_Block_call_copy_helper(result, aBlock);
result->isa = _NSConcreteMallocBlock;
return result;
}
分析
例子一中对_Block_copy已经论述过了这里不做叙述
这里调用了_Block_call_copy_helper函数
_Block_call_copy_helper
block copy的辅助函数 主要用于copy block捕获的变量
实际上这里有一个寻找copy函数的过程,最终会调用 __main_block_copy_0 函数
后面我会分析_Block_call_copy_helper函数是怎么寻找到__main_block_copy_0的
这里为了让流程更加清晰 暂时内部直接调用__main_block_copy_0
// result在step1创建的堆block的地址 aBlock是原本栈block的地址
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock) {
// copy函数调用
__main_block_copy_0(result, aBlock)
}
分析
result参数在step1创建的堆block的地址 aBlock参数是原本栈block的地址
Step2: copy struct __main_block_impl_0 结构体中的 NSObject *obj 也就是 obj对象
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
// 1、 dst是堆block的地址 src是栈block的地址
_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
// BLOCK_FIELD_IS_OBJECT
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)) {
// 2 3/*BLOCK_FIELD_IS_OBJECT*/ 代表 oc对象类型
case BLOCK_FIELD_IS_OBJECT:
// 3 调用 _Block_retain_object_default函数
_Block_retain_object(object);
// 4 堆block的obj指向栈block结构体中的obj
*dest = object;
break;
default:
break;
}
}
static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
// 一步步跟到这里,发现是空函数,苹果隐藏了一些实现细节函数
static void _Block_retain_object_default(const void *ptr __unused) {
}
分析
- dst是堆block的地址 src是栈block的地址
- 3/BLOCK_FIELD_IS_OBJECT/ 代表 oc对象类型
- 调用 _Block_retain_object_default函数,发现是空函数,苹果隐藏了一些实现细节函数
- 堆block的obj指向栈block结构体中的obj 完成 NSObject *obj 的copy
简化过程
_Block_call_copy_helper 官方源码
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
{
if (auto *pFn = _Block_get_copy_function(aBlock))
pFn(result, aBlock);
}
调用 _Block_get_copy_function
static inline __typeof__(void (*)(void *, const void *))
_Block_get_copy_function(struct Block_layout *aBlock)
{
if (!(aBlock->flags & BLOCK_HAS_COPY_DISPOSE))
return NULL;
void *desc = _Block_get_descriptor(aBlock);
#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
struct Block_descriptor_small *bds =
(struct Block_descriptor_small *)desc;
return _Block_get_relative_function_pointer(
bds->copy, void (*)(void *, const void *));
}
#endif
struct Block_descriptor_2 *bd2 =
(struct Block_descriptor_2 *)((unsigned char *)desc +
sizeof(struct Block_descriptor_1));
return _Block_get_copy_fn(bd2);
}
调用_Block_get_copy_fn
static inline __typeof__(void (*)(void *, const void *))
_Block_get_copy_fn(struct Block_descriptor_2 *desc)
{
return (void (*)(void *, const void *))_Block_get_function_pointer(`desc->copy`);
}
重点在这
desc->copy
继续查找desc
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size;
};
struct Block_descriptor_2 {
// requires BLOCK_HAS_COPY_DISPOSE
BlockCopyFunction copy;
BlockDisposeFunction dispose;
};
typedef void(*BlockCopyFunction)(void *, const void *);
desc是Block_descriptor_2结构体指针
copy是BlockCopyFunction类型的变量
再来看下 clang下的代码
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};
分析
- Block_descriptor_1 Block_descriptor_2 对应__main_block_desc_0结构体
- Block_descriptor_2结构体中copy 对应 __main_block_desc_0结构体中的copy函数
所以 _Block_call_copy_helper函数中最终指向的函数就是 __main_block_copy_0
至此_Block_call_copy_helper 的寻找copy函数的推导过程结束
再来看blocks库对block的完整定义
#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
};
struct Block_descriptor_small {
uint32_t size;
int32_t signature;
int32_t layout;
/* copy & dispose are optional, only access them if
Block_layout->flags & BLOCK_HAS_COPY_DIPOSE */
int32_t copy;
int32_t dispose;
};
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
};
事实上这才是真正的block结构 我们看到的clang编译后block结构体是一个中间代码,但是并不妨碍我们理解block,对比上面的clang转换后的代码,我们发现不同的是一些变量在不同的结构体中,但总体数据的顺序是一致的
在说下_Block_get_copy_function 函数
_Block_get_copy_function(struct Block_layout *aBlock)
{
// 这里首先验证flags 是否为 BLOCK_HAS_COPY_DISPOSE
if (!(aBlock->flags & BLOCK_HAS_COPY_DISPOSE))
return NULL;
void *desc = _Block_get_descriptor(aBlock);
struct Block_descriptor_2 *bd2 =
(struct Block_descriptor_2 *)((unsigned char *)desc +
sizeof(struct Block_descriptor_1));
return _Block_get_copy_fn(bd2);
}
分析
发现源码的几个核心函数中 都围绕这个tag值做了很多case语句和条件语句区分
首先验证Block_layout 的 flags 是否为 BLOCK_HAS_COPY_DISPOSE
BLOCK_HAS_COPY_DISPOSE 是什么
查找Block_layout 源码 对flags的定义
// Values for Block_layout->flags to describe block objects
// 是描述 Block_layout结构体 flags成员变量的
enum {
BLOCK_DEALLOCATING = (0x0001), // runtime
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
};
还记得我们看Clang代码汇总的问题4吗
clang代码
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *obj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_obj, int flags=0) : obj(_obj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 570425344 传递给了 __main_block_impl_0 的Flags
// __main_block_impl_0 的Flags 对应 Block_layout 的 flags
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, 570425344));
__block_impl的Flags参数 设置成570425344 代表什么?
上计算机
发现 第 25 29位都是1 也就是 (1 << 25)I(1 << 29)
所以 flag 570425344 代表 BLOCK_HAS_COPY_DISPOSE |BLOCK_USE_STRET
几个重要flags 含义
- BLOCK_NEEDS_FREE表示Malloc
- BLOCK_IS_GLOBAL表示Global
- BLOCK_HAS_COPY_DISPOSE是标记struct __main_block_impl_0是否需要针对内部结构再深入拷贝
- BLOCK_USE_STRET 使用结构体变量
随着分析的过程,4个问题我们已经回答了
例子三 __block 修饰auto类型为基本数据类型
示例代码
__block int age = 6;
void(^block)(void) = ^{
NSLog(@"a==%d",age);
};
Clang
// 570425344
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
再次看到 这个 570425344 我们就知道他代表的意义了
简化后的copy流程
Step1: copy struct __main_block_impl_0
// 第一层 copy block copy
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
aBlock = (struct Block_layout *)arg;
size_t size = Block_size(aBlock);
struct Block_layout *result = (struct Block_layout *)malloc(size);
if (!result) return NULL;
memmove(result, aBlock, size);
result->invoke = aBlock->invoke;
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);
result->flags |= BLOCK_NEEDS_FREE | 2;
_Block_call_copy_helper(result, aBlock);
result->isa = _NSConcreteMallocBlock;
return result;
}
寻找copy函数
在例子2中 已经论述了推导过程 这里不再叙述
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock) {
__main_block_copy_0(result, aBlock)
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->obj, (void*)src->obj, BLOCK_FIELD_IS_BYREF);
}
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_BYREF:
*dest = _Block_byref_copy(object);
break;
default:
break;
}
}
Step2: copy struct __Block_byref_age_0
// 第二层 copy block变量 copy
static struct Block_byref *_Block_byref_copy(const void *arg) {
// __Block_byref_age_0类型强转成Block_byref类型
struct Block_byref *src = (struct Block_byref *)arg;
// 堆空间开启内存
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
// isa 指定为null
copy->isa = NULL;
// 设置引用计数为4
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
// 堆内存的block forwarding指针指向自己
copy->forwarding = copy;
// 栈内存的block forwarding指针堆内存block地址
src->forwarding = copy;
// 设置堆block结构体大小
copy->size = src->size;
// bitcopy src+1即size后面的数据 src+1 其实就是成员 age copy结构体中size变量后面的age到新的Block_byref中去
memmove(copy+1, src+1, src->size - sizeof(*src));
return src->forwarding;
}
分析
- __Block_byref_age_0类型强转成Block_byref类型
- 堆空间开启内存
- isa 指定为null
- 设置引用计数为4
- 堆内存的block forwarding指针指向自己
- 栈内存的block forwarding指针堆内存block地址
- 设置堆block结构体大小
- bitcopy src+1即size后面的数据 src+1 其实就是成员 age copy结构体中size变量后面的age到新的Block_byref中去
copy流程结束
简化流程
_Block_byref_copy函数的简化流程
clang 结构体
// __flags 初始化为0
__Block_byref_age_0 age = {
(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 6};
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
源码结构体
struct Block_byref {
void * __ptrauth_objc_isa_pointer isa;
struct Block_byref *forwarding;
volatile int32_t flags; // contains ref count
uint32_t size;
};
// 1、这个结构体不一定存在
struct Block_byref_2 {
// requires BLOCK_BYREF_HAS_COPY_DISPOSE
BlockByrefKeepFunction byref_keep;
BlockByrefDestroyFunction byref_destroy;
};
struct Block_byref_3 {
// requires BLOCK_BYREF_LAYOUT_EXTENDED
// 2、这个就是block修饰的变量
const char *layout;
};
分析
- Block_byref结构体的flags 对应 __Block_byref_age_0 中的__flags
__flags 初始化为0 - 当 __flags = BLOCK_BYREF_HAS_COPY_DISPOSE 才会存在 Block_byref_2
当 __flags = BLOCK_BYREF_LAYOUT_EXTENDED 才会存在 Block_byref_3 - 所以 _Block_byref_copy函数中的 BLOCK_BYREF_HAS_COPY_DISPOSE BLOCK_BYREF_LAYOUT_EXTENDED 的逻辑直接被简化了
例子四 __block 修饰auto类型为对象类型
示例代码
__block NSObject *obj = [NSObject new];
void(^block)(void) = ^{
NSLog(@"a==%@",obj);
};
简化后的copy流程
Step1: copy struct __main_block_impl_0
// 第一层 copy block copy
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
aBlock = (struct Block_layout *)arg;
size_t size = Block_size(aBlock);
struct Block_layout *result = (struct Block_layout *)malloc(size);
if (!result) return NULL;
memmove(result, aBlock, size);
result->invoke = aBlock->invoke;
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);
result->flags |= BLOCK_NEEDS_FREE | 2;
_Block_call_copy_helper(result, aBlock);
result->isa = _NSConcreteMallocBlock;
return result;
}
寻找 copy函数
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock) {
__main_block_copy_0(result, aBlock)
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->obj, (void*)src->obj, BLOCK_FIELD_IS_BYREF);
}
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_BYREF:
*dest = _Block_byref_copy(object);
break;
default:
break;
}
}
Step2: copy struct __Block_byref_obj_0
Block_byref + Block_byref_2 + Block_byref_3 构成 __Block_byref_obj_0
static struct Block_byref *_Block_byref_copy(const void *arg) {
// c/c++里面 父指子 前4个字节顺序一样 所以类型可以强转
// __Block_byref_obj_0类型强转成Block_byref类型
struct Block_byref *src = (struct Block_byref *)arg;
// 堆空间开启内存 size属性表示结构体的大小
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
// isa 指定为null
copy->isa = NULL;
// 设置引用计数为4
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
// 堆内存的block forwarding指针指向自己
copy->forwarding = copy;
// 栈内存的block forwarding指针堆内存block地址
src->forwarding = copy;
// 设置堆block结构体大小
copy->size = src->size;
// src+1即size后面的数据 这里为 Block_byref_2结构体 复制其copy和release函数
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;
// src2+1这里为 Block_byref_3结构体 也就是成员变量obj 复制obj到堆block
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;
// 这里为了不影响流程分析 暂时内部直接调用__Block_byref_id_object_copy_131
__Block_byref_id_object_copy_131(copy, src);
return src->forwarding;
}
分析
- __Block_byref_obj_0类型强转成Block_byref类型
- 堆空间开启内存 size属性表示结构体的大小
- isa 指定为null
- 设置引用计数为4
- 堆内存的block forwarding指针指向自己
- 栈内存的block forwarding指针堆内存block地址
- 设置堆block结构体大小
- src+1即size后面的数据 这里为 Block_byref_2结构体 复制其copy和release函数
- src2+1这里为 Block_byref_3结构体 也就是成员变量obj 复制obj到堆block
- 这里为了不影响流程分析 暂时内部直接调用__Block_byref_id_object_copy_131
为了让流程更清晰暂时内部直接调用__Block_byref_id_object_copy_131
实际上这里有一个寻找__Block_byref_id_object_copy_131函数的过程
寻找过程后面再说
我们接着往下看
Step3: copy struct __Block_byref_obj_0 内的 NSObject *obj
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
分析
- dst 堆Block_byref结构体的地址 src栈Block_byref结构体的地址
- 40表示__Block_byref_a_0对象obj的位移(4个指针(32字节)+2个int变量(8字节)=40字节)
- 131代表BLOCK_BYREF_CALLER(128)+BLOCK_FIELD_IS_OBJECT(3)
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_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
*dest = object;
break;
default:
break;
}
}
分析
让堆Block_byref结构体成员obj指向原来栈上 obj指向的地址
3层copy流程全部结束
简化过程
简化一
进入void _Block_object_assign(void *destArg, const void *object, const int flags) 函数
Block_layout 的 flags 为 BLOCK_FIELD_IS_BYREF
clang
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src{
_Block_object_assign((void*)&dst->obj, (void*)src->obj, 8/*BLOCK_FIELD_IS_BYREF*/);
}
跳入 _Block_object_assign函数 选择 case BLOCK_FIELD_IS_BYREF
简化二
进入static struct Block_byref *_Block_byref_copy(const void *arg) 函数
Block_byref 的 flags 为 BLOCK_BYREF_HAS_COPY_DISPOSE
Clang
// 33554432
// __flags 初始化为33554432
__attribute__((__blocks__(byref))) __Block_byref_obj_0 obj = {
(void*)0,(__Block_byref_obj_0 *)&obj, 33554432, sizeof(__Block_byref_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"))};
上计算机
发现33554432 就是 ( 1 << 25)
struct Block_byref {
void * __ptrauth_objc_isa_pointer isa;
struct Block_byref *forwarding;
volatile int32_t flags; // contains ref count
uint32_t size;
};
enum {
BLOCK_BYREF_LAYOUT_MASK = (0xf << 28), // compiler
BLOCK_BYREF_LAYOUT_EXTENDED = ( 1 << 28), // compiler
BLOCK_BYREF_LAYOUT_NON_OBJECT = ( 2 << 28), // compiler
BLOCK_BYREF_LAYOUT_STRONG = ( 3 << 28), // compiler
BLOCK_BYREF_LAYOUT_WEAK = ( 4 << 28), // compiler
BLOCK_BYREF_LAYOUT_UNRETAINED = ( 5 << 28), // compiler
BLOCK_BYREF_IS_GC = ( 1 << 27), // runtime
BLOCK_BYREF_HAS_COPY_DISPOSE = ( 1 << 25), // compiler
BLOCK_BYREF_NEEDS_FREE = ( 1 << 24), // runtime
};
简化3
再次回到 void _Block_object_assign(void *destArg, const void *object, const int flags) 函数
Block_layout的flags为 BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT
clang
// __Block_byref_id_object_copy_131
// 参数131代表BLOCK_BYREF_CALLER(128)+BLOCK_FIELD_IS_OBJECT(3)
__Block_byref_obj_0 obj = {
(void*)0,(__Block_byref_obj_0 *)&obj, 33554432, sizeof(__Block_byref_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"))};
源码
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_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
BLOCK_FIELD_IS_WEAK = 16, // declared __weak, only used in byref copy helpers
BLOCK_BYREF_CALLER = 128, // called from __block (byref) copy/dispose support routines.
};
以上就是简化逻辑
下面我们把各种情况串一起 整体分析一下 几个核心函数
_Block_copy
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
// 1、为null 返回 null
if (!arg) return NULL;
// 2、将传递进来的Block强制转换为Block_layout结构
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
// 3 判断如果是堆Block那么就增加引用计数,然后直接返回Block
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
// 4、 如果是全局Block,则无需执行任何操作,直接返回这个Block
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock; // gbobal 直接返回
}
else {
// 5 如果到了这里,那么必然是栈Block。这种情况下,需要将Block复制到堆中。在第一步中,molloc()用于创建所需大小的内存空间,如果内存分配失败,那么直接返回NULL,否则就继续执行
// Its a stack block. Make a copy. // 如果是栈block 进行copy
size_t size = Block_size(aBlock);
// 1、 malloc分配内存区域
struct Block_layout *result = (struct Block_layout *)malloc(size);
if (!result) return NULL;
// 2、 然后复制size大小的数据到新的block中 memmove是把一块数据 全部一一复制 result 是堆上新间首地址 aBlock是栈区首地址
memmove(result, aBlock, size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
// 3、 重新定义调用指针,因为它使用地址身份验证
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
// 4、更新Block的flags,重置引用计数,确保引用计数为0
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
//5、将flag设置为BLOCK_NEEDS_FREE Malloc 将flag加2,表示引用计数加1(堆一次 栈一次)
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
// 6、调用Block中的辅助函数(通常是用来拷贝Block中捕获的参数的)处理其他数据
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
// 7、 将Block的isa设置为_NSConcreteMallocBlock
// isa置为_NSConcreteMallocBlock
result->isa = _NSConcreteMallocBlock;
//8、 返回帮我们copy的新的堆block
return result;
}
}
作用
- 为null 返回 null
- 将传递进来的Block强制转换为Block_layout结构
- 判断如果是堆Block那么就增加引用计数,然后直接返回Block
- 如果是全局Block,则无需执行任何操作,直接返回这个Block
- 如果到了这里,那么必然是栈Block。这种情况下,需要将Block复制到堆中。在第一步中,molloc()用于创建所需大小的内存空间,如果内存分配失败,那么直接返回NULL,否则就继续执行
- malloc分配内存区域
- 然后复制size大小的数据到新的block中 memmove是把一块数据 全部一一复制 result 是堆上新间首地址 aBlock是栈区首地址
- 重新定义调用指针,因为它使用地址身份验证
- 更新Block的flags,重置引用计数,确保引用计数为0
- 将flag设置为BLOCK_NEEDS_FREE Malloc 将flag加2,表示引用计数加1(堆一次 栈一次) 递增为2
- 调用Block中的辅助函数_Block_call_copy_helper(通常是用来拷贝Block中捕获的参数的)处理其他数据
- 将Block的isa设置为_NSConcreteMallocBlock
- 返回帮我们copy的新的堆block
_Block_object_assign
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)) {
// oc对象
case BLOCK_FIELD_IS_OBJECT:
/*******
id object = ...;
[^{ object; } copy];
********/
_Block_retain_object(object);
*dest = object;
break;
// block对象
case BLOCK_FIELD_IS_BLOCK:
/*******
void (^object)(void) = ...;
[^{ object; } copy];
********/
*dest = _Block_copy(object);
break;
// __weak __block 对象
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
// __block对象
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); // *dest指向堆上的byref对象地址 最后返回 src->forwarding ,也就是堆上的byref对象
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_byref_copy
static struct Block_byref *_Block_byref_copy(const void *arg) {
struct Block_byref *src = (struct Block_byref *)arg;
// 是否为0 是否是第一次copy
if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// 初始化为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;
// 判断是否有copy和release函数。没有函数其实就是基本数据类型的变量
// src+1即size后面的数据
// copy结构体中size变量后面的数据到新的Block_byref中去
// 有则先复制其copy和release函数,如果有变量值则再将值赋给新的block,然后调用copy函数
if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
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;
// 如果有变量值则再将值赋给新的block
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;
}
(*src2->byref_keep)(copy, src);
}
else {
// Bitwise copy. //位copy
// This copy includes Block_byref_3, if any.
//如果不需要内存管理,说明捕获的是一个变量,没有Block_byref_2,Block_byref 中size之后就直接是layout了,所以src+1取出的就是layout。直接进行字节拷贝,大小是 src->size - sizeof(*src) 总大小减Block_byref 的大小,就是layout的大小
memmove(copy+1, src+1, src->size - sizeof(*src));
}
}
// 已经copy到堆了 只增加引用计数
// 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;
}
作用
- 是否为0 是否是第一次copy
- 已经copy到堆了 只增加引用计数
- 没有被copy到堆则进入进行 copy Block_byref
- 判断是否有copy和release函数, 没有函数其实就是基本数据类型的变量
- 有则先复制其copy和release函数
- 检测是否存在Block_byref_3 如果有变量值则再将值赋给新的block
- 调用Block_byref 的copy函数
- 如果不需要内存管理,说明捕获的是一个变量,没有Block_byref_2,Block_byref 中size之后就直接是layout了,所以src+1取出的就是layout。直接进行字节拷贝,大小是 src->size - sizeof(*src) 总大小减Block_byref 的大小,就是layout的大小
借用网上的一张图完整流程如下
对流程的一些说明:
1.desc中copy是否存在是根据block中引用的外部变量的类型决定的,基本数据类型是没有copy的。
2.调用_Block_object_assign函数时,变量类型是事先指定好的。
3.byref中copy是否存在是根据__block修饰的变量类型决定的,基本数据类型是没有copy的