为什么 nil / NULL 块在运行时会导致总线错误?

2024-01-11

我开始大量使用块,很快就注意到 nil 块会导致总线错误:

typedef void (^SimpleBlock)(void);
SimpleBlock aBlock = nil;
aBlock(); // bus error

这似乎违背了 Objective-C 忽略 nil 对象消息的通常行为:

NSArray *foo = nil;
NSLog(@"%i", [foo count]); // runs fine

因此,在使用块之前我必须诉诸通常的 nil 检查:

if (aBlock != nil)
    aBlock();

或者使用虚拟块:

aBlock = ^{};
aBlock(); // runs fine

还有其他选择吗?为什么 nil 块不能简单地成为 nop 是有原因的吗?


我想用更完整的答案对此进行更多解释。首先让我们考虑一下这段代码:

#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {    
    void (^block)() = nil;
    block();
}

如果你运行这个,你会看到崩溃block()看起来像这样的行(当在 32 位架构上运行时 - 这很重要):

EXC_BAD_ACCESS(代码=2,地址=0xc)

那么,这是为什么呢?嗯,0xc是最重要的一点。崩溃意味着处理器已尝试读取内存地址处的信息0xc。这几乎肯定是完全错误的做法。那里不太可能有什么东西。但为什么它要尝试读取这个内存位置呢?嗯,这是由于块在引擎盖下实际构建的方式造成的。

定义块时,编译器实际上在堆栈上创建一个结构,其形式如下:

struct Block_layout {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    struct Block_descriptor *descriptor;
    /* Imported variables. */
};

该块是指向该结构的指针。第四位成员,invoke,这个结构是有趣的。它是一个函数指针,指向保存块实现的代码。因此,当调用块时,处理器会尝试跳转到该代码。请注意,如果您计算结构中之前的字节数invoke成员,你会发现十进制有12,或者十六进制有C。

因此,当调用一个块时,处理器会获取该块的地址,加 12 并尝试加载该内存地址中保存的值。然后它尝试跳转到该地址。但如果该块为零,那么它将尝试读取地址0xc。显然,这是一个错误的地址,因此我们得到了分段错误。

现在,它一定是这样的崩溃,而不是像 Objective-C 消息调用那样默默地失败,这实际上是一种设计选择。由于编译器正在决定如何调用该块,因此它必须在调用块的每个地方注入 nil 检查代码。这会增加代码大小并导致性能下降。另一种选择是使用蹦床来进行 nil 检查。然而,这也会导致性能损失。 Objective-C 消息已经通过了蹦床,因为它们需要查找实际将被调用的方法。运行时允许延迟注入方法和更改方法实现,因此无论如何它已经经历了蹦床。在这种情况下,进行 nil 检查的额外惩罚并不重要。

欲了解更多信息,请参阅我的blog http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/ posts http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-2/.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 nil / NULL 块在运行时会导致总线错误? 的相关文章

  • 返回一个dispatch_async获取的变量[重复]

    这个问题在这里已经有答案了 基本上 一个方法需要返回一个在dispatch async中获取的NSDictionary 这是我尝试过的 NSDictionary fetchNSDictionary dispatch queue t Queu
  • 如何检测 UISearchBar/UITextField 输入中的暂停?

    我有以下 UISearchbar 代码 void searchBar UISearchBar searchBar textDidChange NSString searchText UIApplication sharedApplicati
  • 显示器连接或断开连接时的通知

    我正在开发一个 OS X 应用程序 该应用程序在所有连接的显示器的所有可用空间上显示自定义窗口 我可以通过调用 NSScreen Screens 获取可用显示对象的数组 我目前缺少的是一种判断用户是否将显示器连接到系统或从系统断开屏幕的方法
  • 检查系统时间是自动还是用户设置

    我需要在当前的项目中设置用户证明时间 我围绕这个问题发现了很多不同的问题 但似乎没有一个问题有我正在寻找的答案 这些是我到目前为止看过的问题 XCODE 如何从设备获取 验证准确的时间戳 https stackoverflow com qu
  • AppDelegate 的变量用作全局变量不起作用

    我想使用我的 AppDelegate 来存储任何其他类都可以访问的对象 我已经像这样声明了这个 AppDelegate interface MyAppDelegate UIResponder
  • CGContextSelectFont 等效项

    在 iOS 7 CGContext SelectFont 中已弃用 已弃用的消息说我必须使用 Core Text 但我不知道哪一个与这段代码完全相同 CGContextSelectFont context Helvetica kBarLab
  • let/var 如何解决可变性? [复制]

    这个问题在这里已经有答案了 我没有任何问题 我只是想对有关可变性的问题进行一些澄清 在 Objective C 中我们会使用例如NSMutableArray得到一个可变数组和NSArray得到一个不可变的 我对两者的内部运作了解不多 但据我
  • 由于 2.23 导致 iOS 应用程序被拒绝 - iOS 数据存储指南

    以下是 Apple 关于拒绝的消息 2 23 应用程序必须遵循 iOS 数据存储指南 否则将被拒绝 2 23 详情 在启动和内容下载时 您的应用程序会存储 6 5 MB 这并不意味着 遵守 iOS 数据存储指南 下一步 请验证只有用户使用您
  • 为具有多个目标和不同平台的项目编写 Podfile

    我正在准备一个支持 OS X 和 iOS 的 Pod 我的 pod 有一些自己的依赖项 这些依赖项在 podspec 文件中定义 因此我使用 Podfile 来管理我用来开发 pod 和运行测试的项目的依赖项 我正在使用 CocoaPods
  • 与新 Apple Music 应用程序中相同的动态状态栏

    是否可以动态着色statusBar这是在新的苹果音乐应用程序 Edit iOS 8 4 中的新 Apple Music 应用程序具有此功能 打开应用程序 选择并播放歌曲 状态栏为白色 向下滑动播放器控制器以查看 我的音乐 控制器 它有黑色状
  • 使用 ZBarSDK 时 iPhone 相机失去自动对焦功能

    我正在开发一个应用程序 用户可以选择是否要扫描条形码或拍摄某物的照片 为了拍照 我正在使用UIImagePickerController照常 为了扫描条形码 我使用 ZbarSDK 1 2ZBarReaderViewController 拍
  • 在成为FirstResponder或resignFirstResponder的情况下将对象保持在键盘顶部?

    我目前在键盘顶部有一个 UITextField 当您点击它时 它应该粘在键盘顶部并平滑地向上移动 我不知道键盘的具体时长和动画类型 所以确实很坎坷 这是我所拥有的 theTextView resignFirstResponder UIVie
  • 对象指针值作为字典的键

    我想使用对象的引用值作为字典的键 而不是对象值的副本 因此 我本质上想在字典中存储与另一个对象的特定实例关联的对象 并稍后检索该值 这可能吗 是不是完全违背了NSDictionary的理念 我可以看出我可能以错误的方式处理这个问题 因为字典
  • 在故事板中将 UITableView 的 rowHeight 设置为 UITableViewAutomaticDimension ?

    在 Xcode 6 中创建 iOS 8 应用程序时 如何设置 UITableViewrowHeight to UITableViewAutomaticDimension In WWDC 2014 第 226 场会议 表和集合视图中的新增功能
  • 如何清除代码中的 NSLog 输出?

    有没有办法清除代码中的控制台输出 thanks 我喜欢的快捷方式 Simply USE K Keyboard Shortcut when you want to clear the NSLOG Data
  • 如何去掉 UIWebView 上的状态栏背景?

    从 iOS 11 开始 当UIWebView全屏时 状态栏上会出现与屏幕颜色相同的假背景UIWebView背景 有人知道如何摆脱它吗 甚至添加IUWebView到故事板并使其全屏将使状态栏背景出现 我一直在尝试编辑 UIWebView 的大
  • 更改导航项(栏)的背景颜色

    有没有一种简单的方法可以更改视图顶部导航项的背景颜色 我有一个基于导航的应用程序 我只希望一个视图获得另一种背景颜色 我主要使用 IB 创建视图 我找到了以下解决方案 未测试 float r 10 float g 55 float b 13
  • 重叠的装载机圆

    我试图重现苹果为应用程序 活动 制作的重叠圆圈 见下图 如果您使用标准贝塞尔路径 起始 结束位置将仅在 0 到 2PI 之间产生影响 例如 如果您尝试填充 4PI 即使使用一些阴影 则无法模拟重叠加载 如何制作类似于苹果解决方案的东西来创建
  • 如何确定 NSTimeInterval 是否发生在任意 NSDate 期间?

    我有一个 NSTimeInterval 我想知道该时间戳是否位于日期的开始和结束之间 基本上我需要能够做类似的事情 NSDate today NSDate date NSTimeInterval myInterval someInterva
  • 如何从 AFNetworking 和 AFJSONRequestOperation 获取可变字典?

    我将 JSONKit 与 AFNetworking 的 AFHTTPClient 带有 AFJSONRequestOperation 一起使用 我似乎无法弄清楚如何触发使用 JSONKit 的 mutableObjectFrom 方法 而不

随机推荐