• NSNull Crash处理 (NullSafe 的原理)


    问题场景

    后端返回的数据中总会出现一些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又无法处理的消息经过如下几步处理:

    1. 创建一个方法缓存,这个缓存会缓存项目中类的所有类名。

    2. 遍历缓存,寻找是否已经有可以执行此方法的类。

    3. 如果有的话,返回这个NSMethodSignature

    4. 如果没有的话,返回nil,接下来会走forwardInvocation:方法。

    5. [invocation invokeWithTarget:nil];将消息转发给nil。

    那么,如何判断NSNull无法处理这个消息呢,在OC中,系统如果对某个实例发送消息之后,它(及其父类)无法处理(比如,没有这个方法等),系统就会发送methodSignatureForSelector消息,如果这个方法返回非空,那么就去执行返回的方法,如果为nil,则发送forwardInvocation消息。

    这样就完成整个转发链了。

    题外话:一般来说,我们不应该在我们的项目中使用NSNull类(大部分NSNull类的来源来自于接口的返回),而使用nil,在来源上,就应该堵上(要么你解析到null进行处理,要么和你的服务端说,不要给我返回null)。

    reference:

    1.https://segmentfault.com/q/1010000005064181

    2.https://github.com/nicklockwood/NullSafe

    3.http://blog.sina.com.cn/s/blog_5c91824f0102ve3c.html

  • 相关阅读:
    Oracle 分区表管理之组合分区(分区索引失效与性能比较以及12.2c分区表新特性)
    博客搬家了
    Java中文件操作
    Java中的LinkedList介绍
    Android数据缓存
    Android QQ空间浏览图片动画特效的实现(※)
    关于android中线性布局的layout_gravity属性
    Android中AutoCompleteTextView的使用
    Android中GridView使用总结
    Android实现自适应正方形GridView(陌陌引导页面效果)
  • 原文地址:https://www.cnblogs.com/H7N9/p/5908149.html
Copyright © 2020-2023  润新知