问题场景
后端返回的数据中总会出现一些NSNull类型,当我们一处理程序就会崩溃,因此想到把返回的数据中的NSNull类型全部转换成@""空字符串
(1)原始的json串:后端返回的json串里面包含类型NSString,NSArray,NSDictionary,NSNull类型。
{"status":1,"service_name":null,"service_id":null,"img_url":"http://api.jgfw.me/assets/uploads/files/","price":null,"num":3,"service_info":{"service_type":null,"service_time":null,"service_detail":null,"customer_name":null,"customer_phone":null,"customer_address":"","new_jishi":"","old_jishi":null,"lat":null,"lon":null},"order_info":{"order_no":"E15031267469289848688","pay_time":null,"order_time":null,"price":0,"order_state":null}}
(2)用SBJson库:json串转换成字典
NSDictionary *jsonDic = [retString JSONValue];
"<null>" 就是NSNull 类型,直接使用会Crash.
摘要
NullSafe is a simple category on NSNull that returns nil for unrecognised messages instead of throwing an exception.
1 // 2 // NullSafe.m 3 // 4 // Version 1.2.2 5 // 6 // Created by Nick Lockwood on 19/12/2012. 7 // Copyright 2012 Charcoal Design 8 // 9 // Distributed under the permissive zlib License 10 // Get the latest version from here: 11 // 12 // https://github.com/nicklockwood/NullSafe 13 // 14 // This software is provided 'as-is', without any express or implied 15 // warranty. In no event will the authors be held liable for any damages 16 // arising from the use of this software. 17 // 18 // Permission is granted to anyone to use this software for any purpose, 19 // including commercial applications, and to alter it and redistribute it 20 // freely, subject to the following restrictions: 21 // 22 // 1. The origin of this software must not be misrepresented; you must not 23 // claim that you wrote the original software. If you use this software 24 // in a product, an acknowledgment in the product documentation would be 25 // appreciated but is not required. 26 // 27 // 2. Altered source versions must be plainly marked as such, and must not be 28 // misrepresented as being the original software. 29 // 30 // 3. This notice may not be removed or altered from any source distribution. 31 // 32 33 34 //Fix issue desc = "<null>";icon = "<null>"; 35 36 37 #import <objc/runtime.h> 38 #import <Foundation/Foundation.h> 39 40 41 #ifndef NULLSAFE_ENABLED 42 #define NULLSAFE_ENABLED 1 43 #endif 44 45 46 #pragma GCC diagnostic ignored "-Wgnu-conditional-omitted-operand" 47 48 49 @implementation NSNull (NullSafe) 50 51 #if NULLSAFE_ENABLED 52 53 - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector 54 { 55 @synchronized([self class]) 56 { 57 //look up method signature 58 NSMethodSignature *signature = [super methodSignatureForSelector:selector]; 59 if (!signature) 60 { 61 //not supported by NSNull, search other classes 62 static NSMutableSet *classList = nil; 63 static NSMutableDictionary *signatureCache = nil; 64 if (signatureCache == nil) 65 { 66 classList = [[NSMutableSet alloc] init]; 67 signatureCache = [[NSMutableDictionary alloc] init]; 68 69 //get class list 70 int numClasses = objc_getClassList(NULL, 0); 71 Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses); 72 numClasses = objc_getClassList(classes, numClasses); 73 74 //add to list for checking 75 NSMutableSet *excluded = [NSMutableSet set]; 76 for (int i = 0; i < numClasses; i++) 77 { 78 //determine if class has a superclass 79 Class someClass = classes[i]; 80 Class superclass = class_getSuperclass(someClass); 81 while (superclass) 82 { 83 if (superclass == [NSObject class]) 84 { 85 [classList addObject:someClass]; 86 break; 87 } 88 [excluded addObject:NSStringFromClass(superclass)]; 89 superclass = class_getSuperclass(superclass); 90 } 91 } 92 93 //remove all classes that have subclasses 94 for (Class someClass in excluded) 95 { 96 [classList removeObject:someClass]; 97 } 98 99 //free class list 100 free(classes); 101 } 102 103 //check implementation cache first 104 NSString *selectorString = NSStringFromSelector(selector); 105 signature = signatureCache[selectorString]; 106 if (!signature) 107 { 108 //find implementation 109 for (Class someClass in classList) 110 { 111 if ([someClass instancesRespondToSelector:selector]) 112 { 113 signature = [someClass instanceMethodSignatureForSelector:selector]; 114 break; 115 } 116 } 117 118 //cache for next time 119 signatureCache[selectorString] = signature ?: [NSNull null]; 120 } 121 else if ([signature isKindOfClass:[NSNull class]]) 122 { 123 signature = nil; 124 } 125 } 126 return signature; 127 } 128 } 129 130 - (void)forwardInvocation:(NSInvocation *)invocation 131 { 132 invocation.target = nil; 133 [invocation invoke]; 134 } 135 136 #endif 137 138 @end
当我们给一个NSNull对象发送消息的话,可能会崩溃(null是有内存的),而发送给nil的话,是不会崩溃的。
作者就是使用了这么一个原理,把发送给NSNull的而NSNull又无法处理的消息经过如下几步处理:
-
创建一个方法缓存,这个缓存会缓存项目中类的所有类名。
-
遍历缓存,寻找是否已经有可以执行此方法的类。
-
如果有的话,返回这个
NSMethodSignature
。 -
如果没有的话,返回nil,接下来会走
forwardInvocation:
方法。 -
[invocation invokeWithTarget:nil];
将消息转发给nil。
那么,如何判断NSNull无法处理这个消息呢,在OC中,系统如果对某个实例发送消息之后,它(及其父类)无法处理(比如,没有这个方法等),系统就会发送methodSignatureForSelector
消息,如果这个方法返回非空,那么就去执行返回的方法,如果为nil,则发送forwardInvocation
消息。
这样就完成整个转发链了。
题外话:一般来说,我们不应该在我们的项目中使用NSNull类(大部分NSNull类的来源来自于接口的返回),而使用nil,在来源上,就应该堵上(要么你解析到null进行处理,要么和你的服务端说,不要给我返回null)。
reference:
1.https://segmentfault.com/q/1010000005064181