为什么它是exc_bad_access
而不是run-time
或compile-time
错误?
我错误地写了"@age"
而不是@"age"
,这引起了我的好奇心。
我对exc_bad_access
的理解是:Bad-Access
是由指针(好的引用)dereferenced
引起的内存位置引起的尚未分配或取消分配或未经授权访问(const
或某事)。
但在这种情况下,我只是将数据写入内存,语法与NS Objective-c
格式不匹配。因此,它应该是运行时错误而不是 Bad-Access 。
我在哪里错过了这个概念?
答案 0 :(得分:24)
获得EXC_BAD_ACCESS的原因是-initWithObjects:
方法期望其所有参数都是有效的Objective-C对象。每个Objective-C对象都以一个小标题开头;这曾经是一个简单的指针,称为isa
,它的类对象(它不一定非常简单,现在你不应该嘲笑自己;有Objective-C运行时API你可以在必要时使用。)
这里没有遇到编译器错误的原因是C / C ++ / Objective-C中没有办法为“varargs”方法或函数指定正确的类型。因此,编译器允许您传递任何类型的参数,假设您知道自己在做什么。
无论如何,在-initWithObjects:
的实现中,它将尝试向您传入的每个对象发送-retain
消息。当它这样做时,它将尝试取消引用{ {1}}指针。对于C字符串,这意味着它将把字符串的前四个或八个字节视为指针。这不太可能有一个好的结果,很可能你马上得到EXC_BAD_ACCESS。即使你很幸运,他们碰巧指向有效的内存,Objective-C运行时也会指望它们指向一个有效的isa
结构,这种结构非常不太可能,并且结果也很可能是一个EXC_BAD_ACCESS。
答案 1 :(得分:3)
事实上,"@age"
是const char*
,因此它似乎与您对exc_bad_access
的描述相符。
答案 2 :(得分:2)
如上所述@"age"
是创建NSString *
的快捷方式 - 它同样是NSObject
的子类。字符串前面的@
告诉编译器创建并返回NSString *
。
"@age"
没有@
前缀,因此编译器会创建一个const char *
,这是C用来表示字符串的内容。请记住,Objective-C是C的严格超集,这意味着任何C代码都将在Objective-C编译器上编译。这也意味着编译器需要保持C的向后兼容性 - 这意味着"@age"
是C字符串,而@"age"
是Objective-C Foundation Kit NSString对象。
关于你的问题:这不是编译器错误的原因是因为NSArray的initWithObjects:
初始化程序没有指定所需的类型。它简单地说了一个指针列表。并且因为@"age"
(NSString *
)都是指针而"@age"
(const char *
)也是指针,编译器不会抱怨。两者都是编译器允许定义initWithObjects:
的方式。
现在你说为什么它不是运行时错误。 EXC_BAD_ACCESS
是运行时错误。您可能意味着为什么没有抛出异常,因为异常和运行时错误存在差异。但是,异常也是运行时错误,但可以检测并执行它们。通常无法对信号(EXC_BAD_ACCESS
)执行操作,并且应用程序将被操作系统终止。
它是运行时错误的原因是因为initWithObject:
期望列出NSObjects
,并且此函数所做的第一件事就是对提供的对象进行一些检查或处理。现在,在Objective-C运行时内部,它基本上是一个C struct
,其中包含有关对象方法和变量的信息。这些结构通常包含一个指向对象超类的指针。你给这个方法的基本上是垃圾。当它试图检查它认为是带有指针和数据的结构时 - 它得到的只是一个四字节缓冲区("age\0"
)。因此,当它尝试作为一个示例取消引用时,该方法所认为的超类指针是一个结构,它将在该四字节缓冲区之外读取,因此您的应用程序将收到EXC_BAD_ACCESS
。
所以你有它。从编译器的角度来看,你没有做错任何事。从运行时的角度来看,你正在为它提供垃圾,它无法检测到它。它只是处理数据,就像它预期的那样。当它这样做时,它超出了你提供的缓冲区的边界,因此得到EXC_BAD_ACCESS
。
希望这能澄清你的好奇心。
答案 3 :(得分:1)
语法@"name"
是字符串文字,与使[[NSString alloc] initWithUTF8String:"name\0"];
成为对象的"@age"
相同
const char*
是NSArray
。 const char*
只能处理对象,因此给它NSLog();
会导致您的应用崩溃。我不确定为什么静态分析器首先没有发现这个问题,我很惊讶,至少没有警告告诉你使用文字,例如{{1}}