简体   繁体   English

在Objective-C和Xcode中抛出的调试异常

[英]Debugging exception thrown in Objective-C and Xcode

I am a long time Microsoft developer and I am new to iPhone development using Xcode. 我是Microsoft的开发人员,我是使用Xcode进行iPhone开发的新手。 So, I am reading a book and going through examples trying to teach myself how to write an iPhone application using Objective-C. 所以,我正在阅读一本书,并通过示例尝试自学如何使用Objective-C编写iPhone应用程序。 All has been good so far, however, once in a while I run into the generic ' objc_exception_throw ' message at runtime. 到目前为止,一切都很好,但是偶尔我会在运行时遇到通用的' objc_exception_throw '消息。 When this happens, the source of this exception is very difficult to find. 发生这种情况时,很难找到此异常的来源。 After some trial and error I found my answer. 经过一些反复试验,我找到了答案。 One of the parameters was misspelled. 其中一个参数拼写错误。

As you can see below, I misspelled the 'otherButtonTitles' parameter by leaving out the second 't' in button. 正如你在下面看到的那样,我错过了'otherButtonTitles'参数,省略了第二个't'按钮。

UIAlertView *alert = [[UIAlertView alloc] 
                      initWithTitle:@"Date and Time Selected" 
                      message:message 
                      delegate:nil
                      cancelButtonTitle:@"Cancel"
                      otherButonTitles:nil];

The reason this took me time to find is that the code built successfully. 这花费我时间的原因是代码构建成功。 Is this normal behavior for the Objective-C compiler? 这是Objective-C编译器的正常行为吗? I am used to having the build fail in the .NET compiler when I make a common syntax error like this. 当我像这样做一个常见的语法错误时,我习惯于在.NET编译器中使构建失败。 Is there a compiler setting I can change to make the built fail when I make these mistakes? 是否有编译器设置我可以更改以在构建这些错误时使构建失败?

First and foremost, open ~/.gdbinit (that's the file called .gdbinit in your home directory - yes, starts with a dot) and put this in it: 首先,打开~/.gdbinit (这是你的主目录中名为.gdbinit的文件 - 是的,以点开头)并将其放入其中:

fb -[NSException raise]
fb objc_exception_throw
fb malloc_error_break

That'll initialize GDB with three default breakpoints, when they occur, GDB will halt your application and show you the stack trace. 这将使用三个默认断点初始化GDB,当它们发生时,GDB将暂停您的应用程序并向您显示堆栈跟踪。 This is very well integrated with Xcode so you'll be able to nicely walk through your code by clicking stack trace elements as soon as an exception occurs somewhere or a malloc fails. 这与Xcode非常好地集成,因此只要在某处发生异常或malloc失败,您就可以通过单击堆栈跟踪元素来很好地遍历代码。

Then, open the Get Info panel on your project (or select your project (top item in the Groups & Files ) and hit cmd-i ), go to the Build tab and set your project's Base SDK to Device - iPhone OS [someversion] . 然后,打开项目的“ Get Info面板(或选择项目(“ Groups & Files中的顶部项目)并点击cmd-i ),转到“ Build选项卡,将项目的Base SDKDevice - iPhone OS [someversion] Scroll all the way to the bottom and find the GCC 4.0 - Warnings section. 一直滚动到底部,找到GCC 4.0 - Warnings部分。 There; 那里; turn on as many warnings as you feel comfortable with, but make sure to turn on Treat Warnings as Errors (this is the equivalent of GCC_TREAT_WARNINGS_AS_ERRORS ). 打开尽可能多的警告,但请确保将“ Treat Warnings as Errors (这相当于GCC_TREAT_WARNINGS_AS_ERRORS )。 Personally, I have it set to this: 我个人认为:

GCC警告构建设置
(source: lyndir.com ) (来源: lyndir.com

You should now be getting compiler warnings for most things you can do wrong in code and the compiler won't let you run the code until you fix them. 您现在应该在代码中遇到大多数错误的编译器警告,并且在您修复代码之前编译器不会让您运行代码。 When things do get past the compiler's nose, you should be able to find the problem easily with GDB breaking at a convenient spot. 当事情确实越过编译器的鼻子时,你应该能够轻松找到问题,GDB在一个方便的位置破解。

You should also look into NSZombie* . 你也应该看看NSZombie* These are environment variables that are very handy for early breaking on bad memory allocation or access situations. 这些是环境变量,非常便于早期破坏不良内存分配或访问情况。 For instance; 例如; wih NSZombieEnabled nothing will truly be released; wih NSZombieEnabled什么都不会真正被释放; on dealloc it'll get overwritten with _NSZombie and should you try to access this dealloced memory again (dereferencing a dealloced pointer) you'll get something to break on in GDB, instead of the call going through like normal, only being issued on random data (which, of course, isn't what you wanted). 在dealloc上它会被_NSZombie覆盖并且如果你再次尝试访问这个解除分配的内存(取消引用一个解除分配的指针),你将在GDB中获得一些东西,而不是正常的调用,只是随机发出数据(当然,这不是你想要的)。 For more info on this, see 有关详细信息,请参阅 http://www.cocoadev.com/index.pl?NSZombieEnabled . http://www.cocoadev.com/index.pl?NSZombieEnabled

Always use the -Werror GCC setting ( GCC_TREAT_WARNINGS_AS_ERRORS = YES ). 始终使用-Werror GCC设置( GCC_TREAT_WARNINGS_AS_ERRORS = YES )。 You should never have warnings in your code and this is an example where the warning is a critical error. 您的代码中永远不应该出现警告,这是警告是严重错误的示例。

Also, if you get an objc_exception_throw , switch to the console (Command-shift-R) and look for the first "low" number address. 此外,如果您获得objc_exception_throw ,请切换到控制台(Command-shift-R)并查找第一个“低”号地址。

2009-04-01 13:25:43.385 CrashExample[41720:20b] Stack: (
    2528013804,
    2478503148,
    2528036920,
    2528053460,
    2358032430,
    11076,
    11880,
    816174880,
    345098340,
    145973440,
    816174880,
)

In this case it would be "11076". 在这种情况下,它将是“11076”。 So type in the console: 所以输入控制台:

info line *11076

That will tell you the line in your code where the exception was thrown. 这将告诉您代码中抛出异常的行。

Misspelled parameters should generally result in a "Warning: such-and-such object does not respond to selector x" in yellow at the line in question. 拼写错误的参数通常应该导致“警告:这样的对象不响应选择器x”在所讨论的行中为黄色。 I believe this is on by default, as I didn't have to change any compiler settings to see these. 我相信默认情况下这是打开的,因为我没有必要更改任何编译器设置来查看这些。

Also, when I encounter an uncaught exception, it's sometimes beneficial to drop into the gdb console (should come up when you execute your app) and type the following to get backtraces for all threads: 此外,当我遇到未捕获的异常时,放入gdb控制台(应该在执行应用程序时出现)并输入以下内容以获取所有线程的回溯有时是有益的:

taa bt taa bt

What you did is not a compile-time error, because the Objective-C runtime checks at runtime if an object can respond to the message you send to it. 您所做的不是编译时错误,因为Objective-C运行时会在运行时检查对象是否可以响应您发送给它的消息。

I recommend adding this build setting to your target or project: 我建议将此构建设置添加到目标或项目中:

GCC_TREAT_WARNINGS_AS_ERRORS = YES

The reason it's not a compilation error, is because it's perfectly valid to send a message not known at compile time to any object (and any object can be configured to handle messages dynamically as well). 它不是编译错误的原因是因为将编译时未知的消息发送到任何对象是完全有效的(并且任何对象都可以配置为动态处理消息)。 All method calls are really messages being sent to objects. 所有方法调用实际上都是发送给对象的消息。

In general, if you see any warnings you should address them, as in most cases they can lead to problems (as you saw). 一般来说,如果你看到任何警告,你应该解决它们,因为在大多数情况下,它们可能会导致问题(如你所见)。 The misleading aspect is here is that if you compile a file once and it has only warnings, if you compile other classes without making changes to the class that has warnings, the warning will not show in the compiler messages. 这里误导的方面是,如果您编译一次文件并且它只有警告,如果您编译其他类而不更改具有警告的类,则警告将不会显示在编译器消息中。 So every now and then you may want to "clean all targets" and build again to make sure you didn't miss any warnings. 因此,您可能偶尔会“清理所有目标”并再次构建,以确保您不会错过任何警告。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM