Block_copy原理

当我们在代码中使用:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int var = 10;
        void (^block)(void) = Block_copy(^{
            printf("var = %d,<%p>\n", var++, &var);
        });
        block();
    }
    return 0;
}

使用命令:clang -rewrite-objc main.m

// 和前一章节一样,只是在main中多了_Block_copy函数
struct __Block_byref_var_0 {
    void *__isa;
    __Block_byref_var_0 *__forwarding;
    int __flags;
    int __size;
    int var;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_var_0 *var; // by ref
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__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_var_0 *var = __cself->var; // bound by ref
    printf("var = %d,<%p>\n", (var->__forwarding->var)++, &(var->__forwarding->var));
}

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    _Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);
}

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[]) {
    /* @autoreleasepool */ 
    { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_var_0 var = {(void*)0,(__Block_byref_var_0 *)&var, 0, sizeof(__Block_byref_var_0), 10};

        // 这里多了_Block_copy函数
        void (*block)(void) = ((void (*)())_Block_copy((const void *)(((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_var_0 *)&var, 570425344)))));


        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

在c++代码中,多了_Block_copy()函数,而这个函数在runtime.c中(已开源)。

runtime.m

_Block_copy()函数

void *_Block_copy(const void *arg) {
    // Block_layout结构体就是block对应的原型
    struct Block_layout *aBlock;
    // 参数为nil,直接return
    if (!arg) return NULL;

    // 把参数结构体指针赋给局部变量
    aBlock = (struct Block_layout *)arg;
    // 如果该Block已经在堆上,refcount+1,然后return
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    // 如果该Block在Data区,直接return,
    // 第一章我们讲到global类型的block使用copy类型不会改变
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }
    else {
        // 如果是栈区的block,先使用malloc在堆区分配内存空间
        struct Block_layout *result = malloc(aBlock->descriptor->size);
        // 分配失败,return
        if (!result) return NULL;
        // 把参数Block中成员按位复制到新开辟的堆里
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
        // 计数复位
        result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
        // 逻辑或0x01,refcount复位1
        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.
        // 把isa类型指向为堆类型Block
        result->isa = _NSConcreteMallocBlock;
        return result;
    }
}

// 在_Block_copy()中调用了该函数
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
{
    /**
        这个Block_descriptor_2结构体就是文章开头c++代码中的__main_block_desc_0_DATA结构体,
        开头__main_block_desc_0_DATA结构体中是不是实现了copy的函数?
        对应的函数是:__main_block_copy_0()
     */
    struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
    if (!desc) return;

    // 这里就调用了__main_block_desc_0_DATA结构体中成员函数copy()
    (*desc->copy)(result, aBlock); // do fixup
}

现在捋一捋调用的顺序:

Block_copy() -> _Block_copy() -> _Block_call_copy_helper() -> *desc->copy()

_Block_copy()函数中在堆区开辟一个内存空间,并把原来在栈区的Block按位逐个复制到堆区,最后还会调用定义Block描述结构体中的copy函数。

_Block_object_assign

回到文章开头,copy函数指向了__main_block_copy_0函数,在该函数中调用_Block_object_assign函数,我们继续看代码:

// _Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);
// 这里destArg和object都是指向var,而var是一个结构体,第三个参数为: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_OBJECT:
            /*******
             id object = ...;
             [^{ object; } copy];
             ********/
            // 如果源参数是一个对象,那么引用计数+1
            _Block_retain_object(object);
            // 把现在指向堆区的指针重新指向原来该数据的内存地址
            *dest = object;
            break;

        case BLOCK_FIELD_IS_BLOCK:
            /*******
             void (^object)(void) = ...;
             [^{ object; } copy];
             ********/
            /**
                如果源参数是一个Block,则调用_Block_copy
                还是和前面一样:
                1. 该源Block在栈区,则会被复制到堆区;
                2. 该源Block在堆区,引用计数+1;
                3. 该源Block在Data,不会发生任何改变
            */ 
            *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];
             ********/
            // 如果在栈区的变量被__block修饰,那么它将被复制到堆区
            *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];
             ********/
            // MRC模式下,使用__block的修饰对象引用计数不会发生任何变化,这样可以避免循环引用
            // 把现在指向堆区的指针重新指向原来该数据的内存地址
            *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];
             ********/
            // Block中引用了用__weak修饰的对象和Block,
            // 把现在指向堆区的指针重新指向原来该数据的内存地址
            *dest = object;
            break;

        default:
            break;
    }
}

results matching ""

    No results matching ""