Advice on how to catch “attempt to insert nil object” from a device needed

谁说胖子不能爱 提交于 2019-12-04 12:43:37

You don't need to add a category to do method swizzling. I was able to isolate a crash like this by method swizzling initWithObjects:forKeys:count: and putting a try/catch around the original method call. Finally I added a breakpoint in the catch section. This allowed me to break and go back up the stack to where the nil value was being used. This code is added at the top of my 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;
}

And then in my app did finish launching method:

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);

One thing to note on the crash message itself.

"attempt to insert nil object from objects[#]" means that either the key or value at index [#] (thinking of a NSDictionary literal list) is nil.

Say I have a dictionary literal

NSDictionary *person = @{@"first":firstName,@"last":lastName,@"email":email"};

Then index[0] would be the first pair in the list index[1] would be the second pair and so on. If either entry in the pair is nil it will trigger this exception with the corresponding index.

assume that you fetch data using JSON from server, some times a field in the JSON data is null,so you may get NSNull after coversion.

so my advice is checking "null" situation in server, if it occurs, return the faild msg ,but not deliver the bad format data to APP.

when APP receive such those data, APP won't know how to handle it. it's already abnormal. it's okay that you can catch the exception and ignore the abnormal data, but my way to do is to prevent it happens from the source.

In most system calls ( iOS) or server communication can be nil, or when you are working , calling function from third party libs. On those cases it is a must to check a nil value imho. Several senior developers, architects use a multiple statement in on line. Very bad behavior, I lernt my lesson, just write in separate line to know which line crashed exactly the code. A lot easier to fix it.

If you can't find in your code. There are loggers, crash reporter libraries. Use any of than and you will get the causes, line number, easy to fix than.

I wouldn't use everywhere nil checking nor try-catch, just in cases mentioned above

Just found this by looking at where I'm creating NSDictionaries and adding a few assertion statements to check if objects are nil.

  1. search for @{@" - start of creating a dictionary using modern syntax, or NSDictionary
  2. Add NSAssert(object != nil, @"Your Object is is nil here"); before inserting objects
  3. Run the app, check console, find the corresponding assert and fix it:

    Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Your Object is is nil here'

I tried to answer the third answer, it does give me help in most cases. However, when my project support framework 64, I met a disastrous Ben collapse, caused by a third party static library. Ben collapse points CFDictionaryContainsKey.

CFDictionaryRef myDictionaryRef = ......
CFDictionaryContainsKey (myDictionaryRef, searchKey [0]). 

When Ben collapse myDictionaryRef is nil. After some testing, I found a problem.

interface __NSPlaceholderDictionary: NSMutableDictionary {
}
- (Id) initWithObjects: (const id *) arg1 forKeys: (const id *) arg2 count: (unsigned int) arg3;

Compare answers

static id safe_initWithObjects (id self, SEL _cmd, const id objects [],   const id <NSCopying> keys [], NSUInteger count) {.....}

Different types of parameters caused Ben collapse. so, I think the correct answer is

  #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);

Thanks answers providers, but also because I have enough reputation 50. I can not comment directly. I hope you give me a vote.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!