今天在群里看到大佬们在讨论一个面试题,问如下代码在 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. 搭建OpenStack私有云准备工作

    Centos7安装完成后克隆其他子节点 首先在VMware中:右击 虚拟机controller-->设置-->添加-->网络适配器,然后做如下设置: 在VMware中操作 点击:克隆 ...

  2. 今天对C语言不常用的小东西的了解

    今天又翻了C语言的书,看到const语句,一时间想不起来到底是干嘛的,看语句const   int   a=1;明白了这是一个支持常量指定类型的定义常量的关键字,作用几乎与#define一毛一样,但# ...

  3. h5微信中视频禁止全屏

    <video id="videoPlayer" v-show="isShowVideo" class="video" ref=&quo ...

  4. 源码解读 Golang 的 sync.Map 实现原理

    简介 Go 的内建 map 是不支持并发写操作的,原因是 map 写操作不是并发安全的,当你尝试多个 Goroutine 操作同一个 map,会产生报错:fatal error: concurrent ...

  5. SpringMVC框架——文件的上传与下载

    使用SpringMVC框架做个小练习,需求: 1.单个图片上传并显示到页面中: 2.多个图片上传并显示到页面中: 3.上传文件后下载文件: 1.pom.xml中添加依赖 <!-- 文件上传 -- ...

  6. jsp(3,6,9) EL表达式及JSTL

    1. jsp 1.1jsp是什么 全称: Java Server Pages,java服务器页面.和Servlet一样,是sun公司定义的一种动态网页开发技术.    特点:基于html模版,可以在h ...

  7. VSCode 配置C++开发环境

    目录 安装VSCode应用程序 安装相关插件 汉化插件 C++编辑器插件 编写配置文件 tasks.json launch.json c_cpp_properties.json 第一步.安装VSCod ...

  8. Android Studio Run/Debug configuration error: Module not specified

    如下图,配置时没有module可选,因此报错error: Module not specified 解决方法: 1.打开根目录的settings.gradle,删除include ':app' 2.在 ...

  9. javascript异步上传图片文件

    html: <form action="url" enctype="multipart/form-data" id="myform"  ...

  10. demo08-js条件运算符

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...