block对copy的实现

前言

上篇文章我们介绍了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的

猜你喜欢

转载自blog.csdn.net/u014641631/article/details/121632278