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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值