今天在群里看到大佬们在讨论一个面试题,问如下代码在 32bit 和 64bit 系统上分别报什么错误:

#import <Foundation/Foundation.h>
int main()
{
    void (^block)(void) = nil;
    block();
    return 0;
}

虽然有大佬一下子说出了答案,但我仍然一脸懵逼,后来经人提醒,这个考察 block 在内存中的结构,于是赶紧做了如下实验终于弄懂了为什么。

  • 实验

    将以上代码保存为 block_test.m ,在命令行编译成 C++:clang -rewrite-objc -fobjc-arc block_test.m,打开同一目录下生成的block_test.cpp文件,截取如下关键代码:
// 注释①
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

int main()
{
    void (*block)(void) = __null;
    // 注释 ②
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    return 0;
}
  • 解释:

    1. 注释①所在的结构体是 block 在内存中的数据结构。
    2. 注释②是一个函数调用语句,可以简化成:((__block_impl *)block)->FuncPtr(block),即调用 __block_impl 类型指针指向的结构体中 FuncPtr 指向的函数,由于该结构体指针指向的内容为 null,因此 FuncPtr指向的位置显然不是一个函数,所以该指针指向的位置会发生一个EXC_BAD_ACCESS 错误。
    3. FuncPtr 指向的位置是多少呢?根据上述__block_impl中各成员变量的排列可知 FuncPtr 的偏移是 sizeof(void *) + sizeof(int) + sizeof(int),另外还需要考虑结构体内存对齐,参考这个链接,所以在 32bit 系统中,偏移:4 + 4 + 4 = 0x0C;64bit 系统中,偏移:8 + 8 = 0x10
  • 结论:

    • 答案:在 32bit 系统中报错:EXC_BAD_ACCESS(address = 0x0C),在 64bit 系统中报错EXC_BAD_ACCESS(address = 0x10)
    • block 本质是一个结构体,在探究 block 相关的问题时,转换成 C++ 代码后往往就可以一目了然,比如weakSelf 为什么可以解决循环引用

block 的内存结构衍生出来的面试题的更多相关文章

  1. 从汇编代码理解 Block 的内存结构

    ❓ 在断点调试 iOS 程序碰到 block 作为函数的形参时,如果想知道该 block 本身的函数签名信息和函数体地址时,有哪些办法?

  2. block本质探寻一之内存结构

    一.代码——命令行模式 //main.m #import <Foundation/Foundation.h> struct __block_impl { void *isa; int Fl ...

  3. Oracle之内存结构(SGA、PGA)

    一.内存结构 SGA(System Global Area):由所有服务进程和后台进程共享: PGA(Program Global Area):由每个服务进程.后台进程专有:每个进程都有一个PGA. ...

  4. [转]oracle学习入门系列之五内存结构、数据库结构、进程

    原文地址:http://www.2cto.com/database/201505/399285.html 1 Oracle数据库结构 关于这个话题,网上一搜绝对一大把,更别提书籍上出现的了,还有很多大 ...

  5. Java内存结构、类的初始化、及对象构造过程

    概述 网上关于该题目的文章已经很多,我觉得把它们几个关联起来讲可能更好理解一下.与其它语言一样,它在执行我们写的程序前要先分配内存空间,以便于存放代码.数据:程序的执行过程其实依然是代码的执行及数据的 ...

  6. [转载] Oracle之内存结构(SGA、PGA)

    2011-05-10 14:57:53 分类: Linux 一.内存结构 SGA(System Global Area):由所有服务进程和后台进程共享: PGA(Program Global Area ...

  7. JVM内存结构之堆、栈、方法区以及直接内存、堆和栈区别

    JVM内存结构之堆.栈.方法区以及直接内存.堆和栈区别 一.  理解JVM中堆与栈以及方法区 堆(heap):FIFO(队列优先,先进先出):二级缓存:*JVM中只有一个堆区被所有线程所共享:对象和数 ...

  8. oracle内存结构

    一.内存结构 SGA(System Global Area):由所有服务进程和后台进程共享: PGA(Program Global Area):由每个服务进程.后台进程专有:每个进程都有一个PGA. ...

  9. JAVA 对象内存结构

    JAVA对象内存结构 HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header).实例数据(Instance Data)和对齐填充(Padding). 对象头 markWo ...

随机推荐

  1. VUE三 vue-router(路由)详解

    前端路由 根据不同的 url 地址展示不同的内容或页面,无需依赖服务器根据不同URL进行页面展示操作 优点 用户体验好,不需要每次都从服务器全部获取,快速展现给用户 缺点 使用浏览器的前进,后退键的时 ...

  2. JSON Serialization/Deserialization in C#

    因为对C#不是特别熟悉,但是最近写个c#的demo,需要对获取的的json字符串进行解析,开始使用Newtonsoft.Json.Linq尝试了以下,但是感觉操作起来比较麻烦,尤其对与JSON结构比较 ...

  3. JVM03——四种垃圾回收算法,你都了解了哪几种

    在之前的文章中,已经为各位带来了JVM的内存结构与堆内存的相关介绍,今天将为为各位详解JVM垃圾回收与算法.关注我的公众号「Java面典」了解更多 Java 相关知识点. 如何确定垃圾 想要回收垃圾, ...

  4. C语言程序设计(三) 简单的算术运算和表达式

    第三章 简单的算术运算和表达式 算数运算符:一元.二元.三元(条件运算符) 1/2是整型除法,1.0/2是浮点数除法 求余运算(%)限定参与运算的两个操作数必须为整数,不能对两个实型数据进行求余运算 ...

  5. 何为内存模型(JMM)?

    前言 任何一门语言都有其语言规范,从逻辑上我们可划分为语法规范和语义规范,语法规范则是描述了如何通过相关语法编写可执行的程序,而语义规范则是指通过语法编写的程序所构造出的具体含义.语言只要具备存储(比 ...

  6. C++ json解决方案

    前段时间用到C++来封装com 因此从数据转换上我采用的Json来当两种语言的传递方式,现做下json的序列化与反序列化方案的总结: Rapidjson 文档地址:http://rapidjson.o ...

  7. Hibernate一对多

    例如:一个用户可以对应多个订单  但一个订单只能对应一个用户 1.创建实体类 2.编写映射文件 <?xml version="1.0"?> <!DOCTYPE h ...

  8. VMWARE虚拟机安装系统提示CPU已被客户机操作系统禁用和secureCUT乱码

    错误:VMWARE虚拟机安装系统提示CPU已被客户机操作系统禁用 改正:找到虚拟机的位置找到下图灰色的部分:打开 .vmx后缀的操作系统配置文件,加入以下代码: cpuid.1.eax = :: 2. ...

  9. 201771010103 陈亚茹 《面向对象程序设计(java)》第一周学习总结

    本人学号<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.com https://w ...

  10. AQS机制

    一,Lock接口 锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源(但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁).在Lock接口出现之前,Java ...