ios - 有关如何从所需设备捕获 "attempt to insert nil object"的建议

标签 ios objective-c runtime objective-c-category swizzling

这是一种情况: Hockeyapp 和 testflight 时不时地提示我

"attempting to insert nil object"

在可变字典/数组中。我知道正确的做法是始终检查 nil,当它有意义时我会这样做。我们的测试人员无法捕捉到这些崩溃,但 AppStore 用户显然可以。

我的猜测是有时服务器在不应该返回 NSNulls 时返回。 因此,不要在这个庞大的项目中到处插入 nil 检查,我的想法是为测试人员创建一个单独的目标,并对集合类使用方法调配。 比如,我将用我的 swizzled_insertObject:atIndex 替换 insertObject:atIndex,如果对象实际上是 nil,我会在它崩溃之前记录/显示描述性报告。

问题是我不能对 __NSPlaceholderDictionary__NSArrayM 使用 swizzling(只是因为我不能在私有(private)类上创建类别),这让我很难过。

所以基本上我是在征求有关如何捕捉那些讨厌的罕见崩溃的建议。 我想到的一种解决方案是使用 try-catch block ,我知道它们在 Objective-c 中很昂贵,所以我不会在生产中使用它们,仅供测试人员使用。但是被 try-catche 包围的方法 - 被 #ifdef-#endif-s 包围的方法将消除代码的所有可读性。所以我正在寻找一个更优雅的解决方案。 谢谢。

更新:不幸的是,堆栈跟踪不是很有描述性,这是我得到的

Exception Type:  SIGABRT
Exception Codes: #0 at 0x3a378350
Crashed Thread:  0

Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[2]'

Last Exception Backtrace:
0   CoreFoundation                      0x321522a3 <redacted> + 163
1   libobjc.A.dylib                     0x39e7a97f _objc_exception_throw + 31
2   CoreFoundation                      0x320a355f <redacted> + 135
3   CoreFoundation                      0x320da0d3 <redacted> + 51
....

最佳答案

您无需添加类别即可进行方法调配。我能够通过方法 swizzling initWithObjects:forKeys:count: 并在原始方法调用周围放置一个 try/catch 来隔离这样的崩溃。最后我在 catch 部分添加了一个断点。这允许我中断并返回堆栈到使用 nil 值的位置。此代码添加在我的 AppDelegate.m 的顶部:

#import <objc/runtime.h>
#import <objc/message.h>
static id safe_initWithObjects(id self, SEL _cmd, const id objects[], const id <NSCopying> keys[], NSUInteger count) {
    id orignialResult = nil;
    @try {
        orignialResult = objc_msgSend(self, @selector(safe_initWithObjects:forKeys:count:), objects, keys, count);
    }
    @catch (NSException *exception) {
        NSLog(@"BUSTED!"); // put breakpoint here
    }

    return orignialResult;
}

然后在我的应用中完成启动方法:

Class target = NSClassFromString(@"__NSPlaceholderDictionary");
class_addMethod(target, @selector(safe_initWithObjects:forKeys:count:), (IMP)&safe_initWithObjects, "@@:**L");

Method m1 = class_getInstanceMethod(target, @selector(safe_initWithObjects:forKeys:count:));
Method m2 = class_getInstanceMethod(target, @selector(initWithObjects:forKeys:count:));
method_exchangeImplementations(m1, m2);

关于ios - 有关如何从所需设备捕获 "attempt to insert nil object"的建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17346046/

相关文章:

ios - 使用 swift 连接 UIImageView 以查看 Images.xcassets 中的 Controller 显示图像

ios - 如何在 iOS 图像上放置 RTLabel?

objective-c - initWithNibName : on OSX? 相当于什么

javascript - 在应用程序启动之前加载json文件并将其保存在全局变量mithrilJs中

ios - Foursquare categoryId 搜索和照片

objective-c - 将图像文件路径保存到 Core Data

ios - 如何使用 NSURLSession 从 block 中获取数据?

c++ - 使用变量值初始化静态有哪些运行时成本?

java - 在 Java 和 C 中在运行时调用名为 "string"的方法

ios - 我如何设置一个计时器,使我的节点可以在 Swift Xcode 中每两秒跳转一次?