iOS 原理探索-Block_copy

Block_copy

首先,我们需要查看Block.h。这里有以下定义:

#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))

可以看到Block_copy,纯粹是一个宏定义,它将传入的参数强制转换为const void *并将其传递给_Block_copy()。

接下来我们在runtime.cpp中找到了关于_Block_copy的定义。

void *_Block_copy(const void *arg) {
    struct Block_layout *aBlock;
    // 1 如果传递进来的是NULL,那么就直接返回一个NULL
    if (!arg) return NULL;
    
    // The following would be better done as a switch statement
    // 2 将传递进来的Block强制转换为Block_layout结构
    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;
    }
    else {
        // Its a stack block.  Make a copy.
        // 5 如果到了这里,那么必然是栈Block。这种情况下,需要将Block复制到堆中。在第一步中,molloc()用于创建所需大小的内存空间,如果内存分配失败,那么直接返回NULL,否则就继续执行
        struct Block_layout *result =
            (struct Block_layout *)malloc(aBlock->descriptor->size);
        if (!result) return NULL;
        // 6 使用memmove()函数用于将当前堆栈分配的Block逐位复制到刚刚在堆上分配好的内存中。
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
        // Resign the invoke pointer as it uses address authentication.
        // 7 重新定义调用指针,因为它使用地址身份验证。
        result->invoke = aBlock->invoke;
#endif
        // 8 更新Block的flags,重置引用计数,确保引用计数为0
        // reset refcount
        result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
        result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
        // 9 调用Block中的辅助函数(通常是用来拷贝Block中捕获的参数的)
        _Block_call_copy_helper(result, aBlock);
        // Set isa last so memory analysis tools see a fully-initialized object.
        // 10 将Block的isa设置为_NSConcreteMallocBlock
        result->isa = _NSConcreteMallocBlock;
        return result;
    }
}

这个方法的作用

  1. 如果传递进来的是NULL,那么就直接返回一个NULL
  2. 将传递进来的Block强制转换为Block_layout结构
  3. 判断如果是堆Block那么就增加引用计数,然后直接返回Block
  4. 如果是全局Block,则无需执行任何操作,直接返回这个Block
  5. 如果到了这里,那么必然是栈Block。这种情况下,需要将Block复制到堆中。在第一步中,molloc()用于创建所需大小的内存空间,如果内存分配失败,那么直接返回NULL,否则就继续执行
  6. 使用memmove()函数用于将当前堆栈分配的Block逐位复制到刚刚在堆上分配好的内存中。
  7. 重新定义调用指针,因为它使用地址身份验证。
  8. 更新Block的flags,重置引用计数,确保引用计数为0
  9. 调用Block中的辅助函数(通常是用来拷贝Block中捕获的参数的)
  10. 将Block的isa设置为_NSConcreteMallocBlock

Block_release

Block_copy对应的另一半是Block_release,实际上是这样一个宏

#define Block_release(...) _Block_release((const void *)(__VA_ARGS__))

让我来看一下Block_release的代码

void _Block_release(const void *arg) {
    // 1. 将指针转换为Block_layout结构
    struct Block_layout *aBlock = (struct Block_layout *)arg;
    if (!aBlock) return;
    // 2. 如果是全局block,那么直接返回
    if (aBlock->flags & BLOCK_IS_GLOBAL) return;
    // 3. 如果不是堆block,那么直接返回
    if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;
    // 4. 处理堆block的引用计数
    if (latching_decr_int_should_deallocate(&aBlock->flags)) {
        // 5. 释放block捕获的变量
        _Block_call_dispose_helper(aBlock);
        _Block_destructInstance(aBlock);
        // 6. 释放堆block
        free(aBlock);
    }
}
  1. 将指针转换为Block_layout结构
  2. 如果是全局block,那么直接返回
  3. 如果不是堆block,那么直接返回
  4. 处理堆block的引用计数
  5. 释放block捕获的变量
  6. 释放堆block
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342