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

发布于 2024-09-09 03:57:51 字数 5984 浏览 1 评论 0原文

最终更新:事实证明,这个问题的解决方案令人沮丧。进入项目内的 .xcodeproj 目录,然后删除 .mode1v3.pbxuser 文件。这解决了它。嘘,Xcode。


首先我要说的是,这不是通常的保留/释放错误。它刚刚开始发生在我的代码的开发分支上,该错误并不存在于主分支上,并且我很难找到指向答案的源代码中的任何差异。所以我就开始讲那些血淋淋的事情了。

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

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

当您稍后调用 [selfnotesViewController] 时,一切都很好。然而,这个方法就停止工作了:

- (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;
}

当这个方法以与其他访问器相同的方式被调用时,它会崩溃,显然是在笔尖加载期间,但也可能在它之前:

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)

当 Simulator 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] 并不能修复该错误 - 请记住,此代码与我的 master 分支上的代码完全相同,它不会崩溃)。

FWIW,我正在使用 XCode 3.2.3 64 位进行构建,编译模拟器 4.0、调试、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 的建议下,我打开了“运行 ->”设置。启用 Guard Malloc 并再次运行该场景。结果基本相同,只是运行时的内部回溯不同。类或 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 分支之间的代码差异进行了彻底的审核,没有发现任何迹象表明我已经踩到了指针,但我一直认为这最有可能发生。

Final Update: Turns out the fix to this problem is a depressing one. Go into the .xcodeproj directory inside your project, and remove the <username>.mode1v3 and <username>.pbxuser files. That fixed it. Boo, Xcode.


I'll start this off by saying that it's not your usual retain/release bug. It just started happening on a development branch of my code, the bug isn't present on the master branch, and I'm having trouble finding any differences in the source that point to the answer. So I'll just get to the gory stuff.

I've got a view controller class. It has a couple of properties on it referencing a couple of other view controllers. Each is loaded via getter accessor as needed, like so:

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

When you later on call [self notesViewController], it's all good. This one, however, just stopped working:

- (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;
}

When this method gets called the same way as the other accessors, it bombs out, apparently during the nib load, but possibly before it:

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)

It would appear that something nasty is happening in the Simulator SDK when firing the nib load method. In fact, possibly way earlier. Over-riding the nib loading initializer with a little NSLog action in the DateFieldController gets us suspiciously nowhere:

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

Suffice to say, neither of these NSLog statements is ever executed.

So what's going on? Why is the runtime blarfing on me? (Switching the call from self.dateFieldController to [self dateFieldController] doesn't fix the bug -- and remember, this code is exactly the same as that on my master branch, where it isn't crashing).

FWIW, I'm building with XCode 3.2.3 64-bit, compiling for Simulator 4.0, Debug, i386 arch (on a new Mac Book Pro running OS X 10.6.3), Deployment Target in Project Settings is set to "iPhone OS 3.0" (uppping this to 3.1.3 doesn't fix it either).

Update: as requested, the implementation of 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;
}

I left this out earlier because I don't think it's particularly relevant, the problem seems to be happening later in the stack, in the actual getter method dateFieldController.

Update II: At @zneak's suggesting, I turned on the setting Run -> Enable Guard Malloc and ran through the scenario again. The results are basically the same, but the internal backtrace in the runtime is different. It would appear that something's wrong with the Class or the NIB that's preventing it from being decoded:

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)

Update III: In which the plot thickens. At @ohorob's suggestion in the comments, I added the env arg OBJC_PRINT_INITIALIZE_METHODS=YES and found that every single class initialization in the app looks good except the one in question here. Each looks about like this in the console output:

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

When I run the app and select the table row that leads to the explosion, we just see the nslog console output before the crash, and the crash itself. I'd like to think this means we could rule out improper initialization, but I'm not enough of an expert on the runtime.

Did a thorough audit of code differences between master branch and this dev branch, and found nothing indicating that I've stepped on a pointer just yet, but I keep thinking that's the mostly likely think that's going on.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

呆萌少年 2024-09-16 03:57:56

在类初始化期间有些东西变得模糊。

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

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

Something is going squidgy during class initialization.

Try setting the OBJC_PRINT_INITIALIZE_METHODS environment variable to YES and running your application. That might give us a clue. Or not. (Set the environment variable in Xcode's executable inspector -- see the docs).

Do you have any +initialize methods? (in any class -- it may be that one of your classes is tickling the initialization of something seemingly unrelated to the crash)

眼前雾蒙蒙 2024-09-16 03:57:56

问题不断出现的事实让我怀疑有什么东西正在破坏运行时的内部类数据结构。我首先怀疑的是班级过于聪明。你没有任何用isa玩游戏的课程吗?请参阅 XMPPMessage.m 了解这方面的例子。我不是在问这些视图控制器类是否可以与isa一起使用;我的意思是系统中的任何类,因为错误地执行此操作可能会导致它以令人惊讶的方式占用内存。

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

The fact that the problem keeps moving around makes me suspect something is stomping on the runtime's internal class data structures. The first thing I'd suspect would be over-clever classes. You don't have any classes that play games with isa do you? See XMPPMessage.m for an example of this. I'm not asking if these view controller classes play with isa; I mean any class in the system since doing this wrong can cause it to stomp on memory in surprising ways.

Next I'd try using class_copyMethodList() and see if that crashes before calling +alloc. If so, then your class table has definitely been corrupted.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文