没有任何crash发生,在我们来看是最好的:你工作愉快,对您的应用程序,一切都很好!然后突然 - 噗! - 崩溃。 aaargh!! (提示悲伤的小提琴。)
首先要做的是:不要惊慌!
修复崩溃并不需要是很难的。如果你吓坏了,并开始随意改变事情,你很可能使局势恶化;你如果期望只说出正确的咒语,希望错误会奇迹般地消失,你在做梦。相反,你需要采取有条不紊的方法,并学习如何通过自己的方式找崩溃的原因。
首先是为了找出确切位置在您的代码崩溃发生在哪个文件,哪一行。 Xcode调试将帮助你,但你需要了解如何使物尽其用,这正是本教程将告诉你!
入门
下载示例项目example project正如你看到的,这是一个错误的程序!当您打开在Xcode的项目,它显示了至少8个编译器的警告,这始终是头痛的问题。顺便说一下,我们对于本教程使用的Xcode4.3,虽然4.2版本应该工作一样。
注:要按照本教程的需要iOS5模拟器上运行的应用程序。如果您的设备上运行的应用程序,你仍然会得到崩溃,但他们可能不会出现相同的顺序。
让我们在模拟器上运行的应用程序,看看会发生什么。
基本上有两种类型:SIGABRT信号(也称为EXC_CRASH)和EXC_BAD_ACCESS(这也可以显示下产生SIGBUS或SIGSEGV的名称)可能发生的崩溃。
SIGABRT信号是有一个相当不错的一个,因为它是一个可控制的崩溃。终止的目的,因为系统识别应用程序的应用程序做的东西是不应该的。
EXC_BAD_ACCESS,另一方面,给调试增加了很多困难,因为它只会发生钻进了一个损坏的状态时,应用程序,通常是由于内存管理的问题。
幸运的是,这个崩溃(许多人还没来)是一个SIGABRT。发出SIGABRT总是一个错误消息,你可以看到在Xcode的调试输出窗格(右下窗口的角落)。 (如果你没有看到调试输出“窗格中,点击中间的图标在您的Xcode窗口右上角显示调试区的视图图标节。如果调试输出窗格仍然是不可见的,你可能挖掘在调试区的顶部中间的图标 - 搜索字段旁边的图标)。在这种情况下,它说是这样的:
我们就看最重要的一行
[UINavigationController setList:]: unrecognized selector sent to instance 0x6a33840 |
在这里,有问题的对象是位于内存地址0x6a33840UINavigationController,方法是setList:。
知道奔溃的原因是好的,但你的行动首先当然是要弄清楚在那个代码中发生此错误。你需要找到源文件的名称和行为不端的代码所在行数。为此,您可以使用调用栈(又称堆栈跟踪或回溯)。
当一个应用程序崩溃的Xcode窗口的左窗格中切换到Debug导航。它显示是活跃在应用程序中的线程,并强调崩溃的线程。通常是线程1,应用程序的主线程,因为在那里,你会做你的大部分工作。如果您的代码使用队列或后台线程,那么应用程序可能会崩溃在其他线程。
目前,Xcode的突出问题的根源在main.m main()函数。这并不能告诉你非常多,所以你必须挖得更深一些。
看到更多的调用堆栈,拖动滑块在底部的调试导航的最右边。在崩溃的时刻,将显示完整的调用堆栈:
构造出调用顺序图:
所有这些函数和方法调用堆栈中,除了为main(),是灰色的。这是因为他们从内置在iOS框架。有没有为他们提供的源代码。
你源代码是唯一在这个堆栈跟踪main.m,所以这是什么Xcode的源代码编辑器中显示,尽管它不是一个真正的崩溃的真正来源。这往往混淆了新的开发,但在一分钟内,我会告诉你如何做,看到它的真实意义。
尝试一下,单击“从堆栈跟踪的其他项目中的任何一个,你会看到一堆汇编代码可能不会给你太大的意义:
Oh, if only we had the source code for that! :-]
异常断点
那么,你如何找到应用程序崩溃的代码行吗?好了,只要你像这样的堆栈跟踪,异常被抛出的应用程序。 (你可以告诉,因为调用堆栈中的职能之一,被命名为objc_exception_rethrow)。
发生异常捕获程序时,做一些不应该做的。你现在看到的是这个异常的后果:应用程序做了错事,已引发异常,Xcode中显示您的结果。理想的情况下,你希望看到的正是这种异常被抛出。
幸运的是,你可以告诉Xcode暂停在刚才那一瞬间的方案,使用一个异常断点。断点是调试工具,停在一个特定的时刻,根据你的计划。在本教程的第二部分,你会看到他们,但现在你会使用特定的断点将暂停程序,只是前一个异常被抛出。
总结来说,下面的步骤就是启用first-chance exception当第一次 错误抛出就断下。。。
At the bottom is a small + button. Click this and select Add Exception Breakpoint:
A new breakpoint will be added to the list:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { MainViewController *viewController = (MainViewController *)self.window.rootViewController; viewController.list = [NSArray arrayWithObjects:@"One", @"Two"]; return YES; }
这样更好了!源代码编辑器现在出现了,从源代码行 - 没有更多的讨厌汇编代码的东西 ,在左边的调用堆栈(您可能需要通过调试导航切换到调用堆栈,这取决于你如何在 Xcode中设置上)看起来也不同。
显然,罪魁祸首就是这行:在AppDelegate中的应用didFinishLaunchingWithOptions:方法:
viewController.list=[NSArray的arrayWithObjects:“一”,“二”;
采取再看看该错误消息:
[UINavigationController的setList:]:无法识别的选择发送到实例0x6d4ed20
在代码中,“viewController.list=东西”呼吁setList:幕后,因为“清单”是上MainViewController类的属性。然而,根据错误信息的ViewController变量不点到MainViewController对象,而是一个UINavigationController - 当然,UINavigationController的没有“名单”属性!这样的事情在这里混了。
Open the Storyboard file to see what the window’s rootViewController property actually points to:
Ah ha! The storyboard’s initial view controller is a Navigation Controller. That explains why window.rootViewController is a UINavigationController object instead of the MainViewController that you’d expect. To fix this, replace application:didFinishLaunchingWithOptions: with
the following:
通过上图,storyboard的第一个就是 root,是navigationcontroller,接下来的第一个scenes才是mainviewcontroller
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions |