美文网首页
Block的辅助拷贝\销毁函数有什么用

Block的辅助拷贝\销毁函数有什么用

作者: lltree | 来源:发表于2019-11-08 18:02 被阅读0次

理解该部分则需要明白Block原理,该部分探究辅助拷贝\销毁函数作用。本文从拷贝函数入手

参考

什么情况下Block从栈拷贝到堆中

在ARC环境下,编译器会自动的判断,把Block自动的从栈copy到堆。比如当Block作为函数返回值的时候,肯定会copy到堆上。

1.手动调用copy
2.Block是函数的返回值
3.Block被强引用,Block被赋值给__strong或者id类型
4.调用系统API入参中含有usingBlcok的方法

以上4种情况,系统都会默认调用copy方法把Block赋复制

注意:(oc中对象默认所有权为__strong),所以把Block赋值给对象则执行copy

为什么需要辅助考本函数

Block引用栈上对象,栈生命周期完成则该对象释放。而Block引用已经释放外部对象则会崩溃,为此Block会把该对象拷贝一份放置到堆中,同时block内部引用该堆中的对象,而辅助拷贝函数则:

其一:拷贝原有对象内容至堆上,
其二:管理原有对象向引用计数

辅助拷贝函数出现时机

Block由于内部引用外部对象,因此就会出现辅助拷贝函数,辅助拷贝函数作用是把引用的对象复制到,

int main(int argc, const char * argv[]) {
     
    __block id block_obj = [[NSObject alloc]init];
    id obj = [[NSObject alloc] init];

    NSLog(@"block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
    
    void (^myBlock)(void) = ^{
        NSLog(@"***Block中****block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
    };
   
    myBlock();
   
    return 0;
}

执行clang -rewrite-objc main.m获取到cpp文件


struct __Block_byref_block_obj_0 {
  void *__isa;
__Block_byref_block_obj_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 id block_obj;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  id obj;
  __Block_byref_block_obj_0 *block_obj; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id _obj, __Block_byref_block_obj_0 *_block_obj, int flags=0) : obj(_obj), block_obj(_block_obj->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_block_obj_0 *block_obj = __cself->block_obj; // bound by ref
  id obj = __cself->obj; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders__h_7gr165cx2qz0nfs1bnd1t5kc0000gp_T_main_71363e_mi_1,(block_obj->__forwarding->block_obj) , &(block_obj->__forwarding->block_obj) , obj , &obj);
    }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
  _Block_object_assign((void*)&dst->block_obj, (void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);
  _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->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);
  _Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {

    __attribute__((__blocks__(byref))) __Block_byref_block_obj_0 block_obj = {(void*)0,(__Block_byref_block_obj_0 *)&block_obj, 33554432, sizeof(__Block_byref_block_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};
    id obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));

    NSLog((NSString *)&__NSConstantStringImpl__var_folders__h_7gr165cx2qz0nfs1bnd1t5kc0000gp_T_main_71363e_mi_0,(block_obj.__forwarding->block_obj) , &(block_obj.__forwarding->block_obj) , obj , &obj);

    void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, (__Block_byref_block_obj_0 *)&block_obj, 570425344));

    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

    return 0;
}

其中栈Block拷贝到堆上过程如下:

/* Copy, or bump refcount, of a block.  If really copying, call the copy helper if present. */
static void *_Block_copy_internal(const void *arg, const int flags) {
    struct Block_layout *aBlock;
    const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;

    //printf("_Block_copy_internal(%p, %x)\n", arg, flags);
    if (!arg) return NULL;
    
    
    // The following would be better done as a switch statement
    aBlock = (struct Block_layout *)arg;
    
    //如果block的flags字段包含BLOCK_NEEDS_FREE,说明是堆Block
    //那么这是一个堆block(稍后你就明白)。
    //这里只需要增加引用计数然后返回原blcok。
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    //BLOCK_IS_GC,MAC开发
    else if (aBlock->flags & BLOCK_IS_GC) {
        // GC refcounting is expensive so do most refcounting here.
        if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == 1)) {
            // Tell collector to hang on this - it will bump the GC refcount version
            _Block_setHasRefcount(aBlock, true);
        }
        return aBlock;
    }
    //如果这是一个全局Block,则b直接返回原Block
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }

    // Its a stack block.  Make a copy.
    //是一个栈Block 需要copy
    if (!isGC) {
        //在堆上申请内存
        struct Block_layout *result = malloc(aBlock->descriptor->size);
        if (!result) return (void *)0;
          //将当前栈上分配的block按位拷贝到我们刚刚创建的堆内存上
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
       
        // reset refcount
        //清楚引用计数
        result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed
        
        //设置了BLOCK_NEEDS_FREE标志位,表明这是一个堆block
        //并设置引用计数为 1
        result->flags |= BLOCK_NEEDS_FREE | 1;
        //block的isa指针被设置为_NSConcreteMallocBlock,说明这是个堆block
        result->isa = _NSConcreteMallocBlock;
        
        //如果block有一个拷贝辅助函数,那么它将被调用
        if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
            //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);
            (*aBlock->descriptor->copy)(result, aBlock); // do fixup
        }
        return result;
    }
    else {
        // Under GC want allocation with refcount 1 so we ask for "true" if wantsOne
        // This allows the copy helper routines to make non-refcounted block copies under GC
        unsigned long int flags = aBlock->flags;
        bool hasCTOR = (flags & BLOCK_HAS_CTOR) != 0;
        
        //在堆上申请内存
        struct Block_layout *result = _Block_allocator(aBlock->descriptor->size, wantsOne, hasCTOR);
        if (!result) return (void *)0;
        
        //将当前栈上分配的block按位拷贝到我们刚刚创建的堆内存上
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
        // reset refcount
        // if we copy a malloc block to a GC block then we need to clear NEEDS_FREE.
        flags &= ~(BLOCK_NEEDS_FREE|BLOCK_REFCOUNT_MASK);   // XXX not needed
      
        if (wantsOne)
            flags |= BLOCK_IS_GC | 1;
        else
            flags |= BLOCK_IS_GC;
        
        result->flags = flags;
        
        if (flags & BLOCK_HAS_COPY_DISPOSE) {
            //printf("calling block copy helper...\n");
            (*aBlock->descriptor->copy)(result, aBlock); // do fixup
        }
        if (hasCTOR) {
            result->isa = _NSConcreteFinalizingBlock;
        }
        else {
            result->isa = _NSConcreteAutoBlock;
        }
        return result;
    }
}

整个拷贝过程如下:

1 arg参数(传进来的Block)出错则返回

2 不同Blcok类型,不同处理方式

2.1 如果拷贝的是堆Block则堆Block引用计数自加1
2.2 如果是MAC上开发的Blcok则处理
2.3 如果这是一个全局Block,则b直接返回原Block

3 拷贝过程

3.1 堆上申请内存
3.2 将当前栈上分配的block按位拷贝到我们刚刚创建的堆内存上
3.3 清楚该Block的引用计数
3.4 设置了BLOCK_NEEDS_FREE标志位,表明这是一个堆block,并设置引用计数为1
3.5 如果block有一个拷贝辅助函数,那么它将被调用

辅助拷贝函数执行内容

辅助拷贝函数入参Block分别是:堆上dst的Block和原src的Block

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
  _Block_object_assign((void*)&dst->block_obj, (void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);
  _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->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);
  _Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

从辅助拷贝函数可以看出主要调用了_Block_object_assign方法,而该方法内部执行

void _Block_object_assign(void *destAddr, const void *object, const int flags) {
    //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);
    if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
        if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
            _Block_assign_weak(object, destAddr);
        }
        else {
            // do *not* retain or *copy* __block variables whatever they are
            _Block_assign((void *)object, destAddr);
        }
    }
    else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)  {
        // copying a __block reference from the stack Block to the heap
        // flags will indicate if it holds a __weak reference and needs a special isa
        _Block_byref_assign_copy(destAddr, object, flags);
    }
    // (this test must be before next one)
    else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) {
        // copying a Block declared variable from the stack Block to the heap
        //拷贝一个Block变量 从栈上到堆上
        _Block_assign(_Block_copy_internal(object, flags), destAddr);
    }
    // (this test must be after previous one)
    else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
        //printf("retaining object at %p\n", object);
        _Block_retain_object(object);
        //printf("done retaining object at %p\n", object);
        _Block_assign((void *)object, destAddr);
    }
}

其中针对捕获不同对象类型,执行不同的操作:

BLOCK_FIELD_IS_BYREF类型(8):拷贝对象被__block修饰的,执行_Block_byref_assign_copy方法,该方法把__block的对象内部也复制到堆上

BLOCK_FIELD_IS_OBJECT类型(3):如果捕获到的是对象,则原有对象引用计数自加1

BLOCK_FIELD_IS_BLOCK类型(7):如果捕获到的是Blcok

BLOCK_FIELD_IS_WEAK类型(16):如果捕获到的是被__weak修饰的变量

可以看出内部有3个判断, 分别调用了 _Block_byref_assign_copy 、 _Block_assign、_Block_retain_object

捕获__Block修饰的对象执行_Block_byref_assign_copy

// 运行时入口点,用于维护分支数据块的共享知识. 复制闭包后, 修改引用var时共享同步byref数据, byref 指针已经复制到堆上, 栈上修改堆上需要同步
// var byref指针是否已经被复制到堆中,如果是的话,之后在用var直接retain引用计数。
// 否则,我们需要复制它, 并更新栈forwarding指针指向
// 参数
/// dest: var地址
/// arg: var对象
/// flags: var类型
static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {
    struct Block_byref **destp = (struct Block_byref **)dest;
    struct Block_byref *src = (struct Block_byref *)arg;
    // 可以看到初始化时将flags赋值为0 代表初次copy var
    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        // 是否是weak(只有在GC) 所以可以忽略 === fasle
        bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));
        /// 如果它的弱请求一个对象(仅在GC下重要)
        // 堆中申请一个src大小的内存并且用byref类型指针指向, 其实就是复制var到堆中
        struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);
        // 下面就是为copy出来的byref 指针赋值而已
        // 非GC用于调用者,一个用于栈, 植入BLOCK_NEEDS_FREE, 保证后期在次引用不会再copy
        copy->flags = src->flags | _Byref_flag_initial_value;
        #/// 重要把栈上得forwarding指针指向堆上,而堆上的forwarding也指向自己
        copy->forwarding = copy; // patch 拷贝堆中指针 指向自己
        src->forwarding = copy;  // patch 栈指向堆拷贝
        copy->size = src->size;
        // 如果是weak(只有在GC) 所以不会进
        if (isWeak) {
            copy->isa = &_NSConcreteWeakBlockVariable;  // 标记isa字段,因此它会弱扫描
        }
        // BLOCK_HAS_COPY_DISPOSE 这个byref var具有自己的copy/dispose辅助函数,而此时我们的内部实现不会进行默认的复制操作
        if (src->flags & BLOCK_HAS_COPY_DISPOSE) {
            copy->byref_keep = src->byref_keep;
            copy->byref_destroy = src->byref_destroy;
            (*src->byref_keep)(copy, src);
        }
        else {
            // just bits.  Blast 'em using _Block_memmove in case they're __strong
            // 只是bits。Blast'em使用_Block_memmove,以防它们是__strong
            _Block_memmove(
                           (void *)&copy->byref_keep,
                           (void *)&src->byref_keep,
                           src->size - sizeof(struct Block_byref_header));
        }
    }
    // already copied to heap
    // 已复制到堆之后在调用
    else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) {
        // 当再次拷贝i时,则仅仅retain其引用计数
        latching_incr_int(&src->forwarding->flags);
    }
    // assign byref data block pointer into new Block
    //将byref数据块指针分配给新的Block
    _Block_assign(src->forwarding, (void **)destp); // 这句仅仅是直接赋值,其函数实现只有一行赋值语句,查阅runtime.c可知
}

捕获的object是一个objective-c 对象

// 如果在MRC下内部会对其retain
static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
static void _Block_retain_object_default(const void *ptr) {
    if (!ptr) return;
}

捕获的object是一个block

static void (*_Block_assign)(void *value, void **destptr) = _Block_assign_default;
static void _Block_assign_default(void *value, void **destptr) {
    *destptr = value;
}

相关文章

  • Block的辅助拷贝\销毁函数有什么用

    理解该部分则需要明白Block原理,该部分探究辅助拷贝\销毁函数作用。本文从拷贝函数入手 参考 什么情况下Bloc...

  • 13拷贝控制

    13拷贝控制 13.1拷贝、赋值与销毁 13.1.1拷贝构造函数 拷贝构造函数的第一个参数必须是引用类型。 使用拷...

  • C++拷贝控制

    前言 C++通过在类中定义几个成员函数来控制的对象的拷贝,移动,赋值和销毁,分别如下: 拷贝构造函数和移动构造函数...

  • 8.iOS开发之block

    block简介 block(闭包)的本质是对象,是带有自动变量(局部变量)的匿名函数。 block与变量 值拷贝 ...

  • block总结

    (存在栈中的block)和(函数)在运行完后就会销毁 在ARC中block可以用strong或copy都行,MRC...

  • C++ 构造函数,类的成员变量

    c++ 05 构造函数无参构造函数有参构造函数 拷贝构造函数 浅拷贝 深拷贝 类的成员变量 四类特殊的成员变量

  • (笔记)C++面向对象高级编程(上) - 2 Boolan

    (有指针Class -String) 1. Big Three: 拷贝构造函数, 拷贝赋值函数, 析构函数 2. ...

  • C++语言基础(02)

    1.可变参数 2.构造函数、析构函数、拷贝构造函数 构造函数 拷贝构造函数 //浅拷贝(值拷贝)问题 //深拷贝

  • CodeIgniter辅助函数简介

    作者:烨竹 目录结构 辅助函数参考 数组辅助函数 验证码辅助函数 Cookie 辅助函数 日期辅助函数 目录辅助函...

  • C++面向对象高级编程(上)-第二周-博览网

    第二周 三大函数:拷贝构造,拷贝赋值,析构 字符串的构造函数,拷贝构造函数, 拷贝构造函数和拷贝赋值函数没有自主定...

网友评论

      本文标题:Block的辅助拷贝\销毁函数有什么用

      本文链接:https://www.haomeiwen.com/subject/ymdwbctx.html