为什么我的程序在objc_msgSend()期间在_class_isInitialized()中接收EXC_BAD_ACCESS?

时间:2010-07-08 15:59:20

标签: iphone objective-c runtime simulator

最终更新:原来解决这个问题的方法令人沮丧。进入项目内的.xcodeproj目录,然后删除<username>.mode1v3<username>.pbxuser文件。修好了。 Boo,Xcode。


我会说这不是你通常的保留/发布错误。它刚刚开始发生在我的代码的开发分支上,主分支上没有错误,我很难找到指向答案的源代码中的任何差异。所以我只是得到了血腥的东西。

我有一个视图控制器类。它有几个属性引用其他几个视图控制器。每个都根据需要通过getter访问器加载,如下所示:

- (NotesViewController *)notesViewController {
    if (notesViewController == nil) {
        notesViewController = [[NotesViewController alloc] initWithNibName:@"NotesViewController" bundle:nil];
    }
    return notesViewController;
}

当你稍后致电[self notesViewController]时,一切都很好。然而,这个停止了工作:

- (DateFieldController *)dateFieldController {
    NSLog(@"made it into the method at least ...");
    if (dateFieldController == nil) {
        NSLog(@"nib loader, don't let us down...");
        dateFieldController = [[DateFieldController alloc] initWithNibName:@"DateFieldController" bundle:nil];
    }
    return dateFieldController;
}

当这个方法以与其他访问器相同的方式被调用时,它会突然显示出来,显然是在nib加载期间,但可能在它之前:

2010-07-08 11:44:58.029 MyApp[24404:207] made it into the method at least ...
2010-07-08 11:44:58.030 MyApp[24404:207] nib loader, don't let us down...
Program received signal:  “EXC_BAD_ACCESS”.
(gdb) bt
#0  0x028bb0ca in _class_isInitialized ()
#1  0x028b9eca in _class_initialize ()
#2  0x028bf1f6 in prepareForMethodLookup ()
#3  0x028b86c9 in lookUpMethod ()
#4  0x028b8836 in _class_lookupMethodAndLoadCache ()
#5  0x028c6ad3 in objc_msgSend ()
#6  0x00007bb3 in -[EntryViewController controllerForFieldType:] (self=0x7942d70, _cmd=0x190c20, type=0x79844b0) at /Users/wgray/Documents/Sources/iPhone/MyApp-iphone/Classes/EntryViewController.m:481
#7  0x00007e07 in -[EntryViewController selectTypesControllerDidSelect:] (self=0x7942d70, _cmd=0x190bb4, type=0x79844b0) at /Users/wgray/Documents/Sources/iPhone/MyApp-iphone/Classes/EntryViewController.m:527
#8  0x0000d2ad in -[TypesViewController tableView:didSelectRowAtIndexPath:] (self=0x5d2aac0, _cmd=0x1eac458, aTableView=0x6056000, indexPath=0x781c780) at /Users/wgray/Documents/Sources/iPhone/MyApp-iphone/Classes/TypesViewController.m:81
#9  0x0059e718 in -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] ()
#10 0x00594ffe in -[UITableView _userSelectRowAtIndexPath:] ()
#11 0x002abcea in __NSFireDelayedPerform ()
#12 0x0274cd43 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ ()
#13 0x0274e384 in __CFRunLoopDoTimer ()
#14 0x026aad09 in __CFRunLoopRun ()
#15 0x026aa280 in CFRunLoopRunSpecific ()
#16 0x026aa1a1 in CFRunLoopRunInMode ()
#17 0x02fd02c8 in GSEventRunModal ()
#18 0x02fd038d in GSEventRun ()
#19 0x0053ab58 in UIApplicationMain ()
#20 0x00001e24 in main (argc=1, argv=0xbffff050) at /Users/wgray/Documents/Sources/iPhone/MyApp-iphone/main.m:14
(gdb)

在触发nib加载方法时,模拟器SDK中似乎发生了令人讨厌的事情。事实上,可能早一点。使用DateFieldController中的一点NSLog操作覆盖nib加载初始化程序会让我们感到怀疑:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    NSLog(@"come on, you...");
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) 
    {
    NSLog(@"rock on!");
  }
  return self;
}

可以说,这些NSLog语句都没有执行过。

那是怎么回事?为什么运行时对我不利? (将呼叫从self.dateFieldController切换到[self dateFieldController]并不能解决问题 - 请记住,此代码与我的主分支上的代码完全相同,但不会崩溃)。

FWIW,我正在使用64位XCode 3.2.3进行构建,为Simulator 4.0,Debug,i386 arch编译(在运行OS X 10.6.3的新Mac Book Pro上),项目设置中的部署目标设置为“iPhone OS 3.0”(将此更新为3.1.3也无法修复)。

根据要求更新:,实施controllerForFieldType:

- (id)controllerForFieldType:(Type *)type {
    id controller = nil;

    if ([type.mode isEqualToString:@"note"]) {
        controller = self.notesViewController;
    } else if ([type.mode isEqualToString:@"date"]) {
        NSLog(@"going for it, attempting to load dateFieldController from accessor...");
        controller = self.dateFieldController;
    } else {
        controller = self.fieldViewController;
    }

    return controller;
}

我之前把它排除在外是因为我觉得它不是特别相关,问题似乎是在堆栈的后期,在实际的getter方法中dateFieldController

更新II: @ zneak建议,我打开设置运行 - &gt;启用Guard Malloc并再次运行该方案。结果基本相同,但运行时的内部回溯是不同的。看起来Class或NIB出现了一些阻止它被解码的错误:

2010-07-08 12:26:26.180 Strip[24961:207] made it into the method at least ...
2010-07-08 12:26:26.289 Strip[24961:207] nib loader, don't let us down...
Program received signal:  “EXC_BAD_ACCESS”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Not safe to call dlopen at this time.)
(gdb) bt
#0  0x028cd41f in attachMethodLists ()
#1  0x028cdf77 in realizeClass ()
#2  0x028cedad in _class_getNonMetaClass ()
#3  0x028c8eb0 in _class_initialize ()
#4  0x028ce1f6 in prepareForMethodLookup ()
#5  0x028c76c9 in lookUpMethod ()
#6  0x028c7836 in _class_lookupMethodAndLoadCache ()
#7  0x028d5ad3 in objc_msgSend ()
#8  0x00007b81 in -[EntryViewController controllerForFieldType:] (self=0x38cfaf30, _cmd=0x190c8f, type=0x3cacbfe0) at /Users/wgray/Documents/Sources/iPhone/strip-iphone/Classes/EntryViewController.m:482
...snip...
#22 0x00001de4 in main (argc=1, argv=0xbfffefec) at /Users/wgray/Documents/Sources/iPhone/strip-iphone/main.m:14
(gdb)

更新III:其中情节变浓。在@ ohorob在评论中的建议中,我添加了env arg OBJC_PRINT_INITIALIZE_METHODS = YES并发现应用程序中的每个类初始化看起来都很好,除了这里讨论的那个。每个在控制台输出中都看起来像这样:

objc[26750]: INITIALIZE: calling +[UIPinchGestureRecognizer initialize]
objc[26750]: INITIALIZE: finished +[UIPinchGestureRecognizer initialize]
objc[26750]: INITIALIZE: UIPinchGestureRecognizer is fully +initialized

当我运行应用程序并选择导致爆炸的表格行时,我们只看到崩溃前的nslog控制台输出和崩溃本身。我想这意味着我们可以排除不正确的初始化,但我还不够运行时的专家。

彻底审核了master分支和这个dev分支之间的代码差异,并没有发现任何迹象表明我已经踩到了指针,但我一直认为这很可能是认为正在进行。

2 个答案:

答案 0 :(得分:2)

在课堂初始化过程中出现了一些问题。

尝试将OBJC_PRINT_INITIALIZE_METHODS环境变量设置为YES并运行您的应用程序。这可能会给我们一个线索。或不。 (在Xcode的可执行检查器中设置环境变量 - 请参阅the docs)。

您有+initialize种方法吗? (在任何一个类中 - 可能是你的一个类正在搔痒看似与崩溃无关的东西的初始化)

答案 1 :(得分:1)

问题不断出现的事实使我怀疑某些东西正在踩踏运行时的内部类数据结构。我怀疑的第一件事就是过于聪明的课程。你没有任何与你玩游戏的课程吗?有关此示例,请参阅XMPPMessage.m。我不是在问这些视图控制器类是否与isa一起玩;我的意思是系统中的任何类,因为这样做会导致它以令人惊讶的方式踩踏内存。

接下来我尝试使用class_copyMethodList(),看看在调用+ alloc之前是否崩溃了。如果是这样,那么你的班级表肯定已经损坏了。