这是一个情况:曲棍球应用程序和试飞时不时地抱怨我
"试图插入 nil 对象">
在可变字典/数组中。我知道正确的做法是一直检查 nil,当它有意义时我会这样做。我们的测试人员无法捕捉到这些崩溃,但AppStore用户显然可以。
我的猜测是,有时服务器在不应该返回NSNulls的时候返回NSNulls。因此,为了不在这个庞大的项目中到处插入对 nil 的检查,我的想法是为测试人员创建一个单独的目标,并对集合类使用方法重排。比如说,我会用我的swizzled_insertObject:atIndex
替换insertObject:atIndex
,如果对象实际上为零,我会在崩溃之前记录/显示描述性报告。
问题是我不能将滑动用于__NSPlaceholderDictionary
或__NSArrayM
(只是因为我无法在私人课程上制作类别(,这让我很难过。
所以基本上我是在征求关于如何抓住那些令人讨厌的罕见车祸的建议。我想到的一个解决方案是使用 try-catch 块,我知道它们在 Objective-c 中很昂贵,所以我不会在生产中使用它们,只用于测试人员。但是被 try-catche
-s 包围的方法被 #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
....
您无需添加类别即可执行方法重排。我能够通过 initWithObjects:forKeys:count: 的方法隔离这样的崩溃,并在原始方法调用周围放置一个尝试/捕获。最后,我在捕获部分添加了一个断点。这允许我中断并返回到堆栈使用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);
崩溃消息本身需要注意的一件事。
">尝试从对象 [#] 插入 nil 对象"意味着索引 [#] 处的键或值(考虑 NSDictionary 文本列表(为 nil。
假设我有一个字典文字
NSDictionary *person = @{@"first":firstName,@"last":lastName,@"email":email"};
然后 index[0] 将是列表索引 [1] 中的第一对,将是第二对,依此类推。 如果对中的任何一个条目为 nil,它将使用相应的索引触发此异常。
我试图回答第三个答案,在大多数情况下它确实给了我帮助。然而,当我的项目支持框架 64 时,我遇到了灾难性的 Ben 崩溃,这是由第三方静态库引起的。Ben 折叠点 CFDictionaryContainsKey.
CFDictionaryRef myDictionaryRef = ......
CFDictionaryContainsKey (myDictionaryRef, searchKey [0]).
当 Ben 折叠时,myDictionaryRef 为零。经过一些测试,我发现了一个问题。
interface __NSPlaceholderDictionary: NSMutableDictionary {
}
- (Id) initWithObjects: (const id *) arg1 forKeys: (const id *) arg2 count: (unsigned int) arg3;
比较答案
static id safe_initWithObjects (id self, SEL _cmd, const id objects [], const id <NSCopying> keys [], NSUInteger count) {.....}
不同类型的参数导致本崩溃。所以,我认为正确的答案是
#import <objc / runtime.h>
#import <objc / message.h>
static id safe_initWithObjects (id self, SEL _cmd, const id * objects, const id * keys, unsigned int 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);
感谢回答提供者,也因为我有足够的声誉 50.我不能直接评论。我希望你给我一票。
假设你从服务器使用JSON获取数据,有时JSON数据中的字段为空,所以你可能会在转换后得到NSNull。
所以我的建议是检查服务器中的"空"情况,如果发生这种情况,则返回失败的消息,但不会将错误格式的数据传递给APP。
当APP收到这些数据时,APP将不知道如何处理。 已经不正常了。您可以捕获异常并忽略异常数据,但我的方法是从源头防止它发生。
在大多数系统调用(iOS(或服务器通信可以是零,或者当你工作时,从第三方库调用函数。在这些情况下,必须检查零值恕我直言。几位资深开发人员,架构师在线使用多个语句。非常糟糕的行为,我忘记了我的教训,只需在单独的行中写字即可知道哪一行确切地崩溃了代码。修复它要容易得多。
如果在代码中找不到。有记录器,崩溃报告器库。使用任何比,你会得到原因,行号,容易修复比。
我不会在任何地方使用零检查或尝试捕获,只是在上面提到的情况下
刚刚通过查看我在哪里创建 NSDictionaries 并添加一些断言语句来检查对象是否为 nil 来发现这一点。
- 搜索
@{@"
- 开始使用现代语法创建词典,或NSDictionary
- 在插入对象之前添加 NSAssert(object != nil, @"Your Object is is nil here"(;
-
运行应用程序,检查控制台,找到相应的断言并修复它:
由于未捕获的异常"NSInternalInconsistencyException"而终止应用程序,原因:"您的对象在这里为零">