Here is a situation: Hockeyapp and testflight every now and then complain about me
"attempting to insert nil object"
in mutable dictionaries/arrays. I know the right thing is to check for nil all the time, and I do when it makes sense.. Our testers can not catch those crashes, but AppStore users obviously can.
My guess is that sometimes server returns NSNulls when it should not.
So not to insert checks for nil everywhere in the huge project my idea was to create a separate target for the testers and use method swizzling for collection classes.
Say, I'll replace insertObject:atIndex
with my swizzled_insertObject:atIndex
, where if the object is actually nil I log/show a descriptive report before it crashes.
The thing is I can not use swizzling for __NSPlaceholderDictionary
or __NSArrayM
(just because I can not make a category on private classes) and that makes me sad.
So basically I'm asking for advice on how to catch those nasty rare crashes.
One solution I have in mind is using try-catch blocks, I know they are expensive in Objective-c, so I'd not use them in production, just for testers. But methods surrounded by try-catche
-s surrounded by #ifdef
-#endif
-s will erase all the readableness of the code. So I'm searching for a more elegant solution.
Thanks.
Update: the stack traces are unfortunaely not very descriptive, here is what I get
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
....
Just found this by looking at where I'm creating NSDictionaries and adding a few assertion statements to check if objects are nil.
@{@"
- start of creating a dictionary using modern syntax, orNSDictionary
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'
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
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
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.
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.
When Ben collapse myDictionaryRef is nil. After some testing, I found a problem.
Compare answers
Different types of parameters caused Ben collapse. so, I think the correct answer is
Thanks answers providers, but also because I have enough reputation 50. I can not comment directly. I hope you give me a vote.
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:
And then in my app did finish launching method:
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.