问题场景

后端返回的数据中总会出现一些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.

 //
// NullSafe.m
//
// Version 1.2.2
//
// Created by Nick Lockwood on 19/12/2012.
// Copyright 2012 Charcoal Design
//
// Distributed under the permissive zlib License
// Get the latest version from here:
//
// https://github.com/nicklockwood/NullSafe
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
// //Fix issue desc = "<null>";icon = "<null>"; #import <objc/runtime.h>
#import <Foundation/Foundation.h> #ifndef NULLSAFE_ENABLED
#define NULLSAFE_ENABLED 1
#endif #pragma GCC diagnostic ignored "-Wgnu-conditional-omitted-operand" @implementation NSNull (NullSafe) #if NULLSAFE_ENABLED - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
@synchronized([self class])
{
//look up method signature
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature)
{
//not supported by NSNull, search other classes
static NSMutableSet *classList = nil;
static NSMutableDictionary *signatureCache = nil;
if (signatureCache == nil)
{
classList = [[NSMutableSet alloc] init];
signatureCache = [[NSMutableDictionary alloc] init]; //get class list
int numClasses = objc_getClassList(NULL, );
Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses);
numClasses = objc_getClassList(classes, numClasses); //add to list for checking
NSMutableSet *excluded = [NSMutableSet set];
for (int i = ; i < numClasses; i++)
{
//determine if class has a superclass
Class someClass = classes[i];
Class superclass = class_getSuperclass(someClass);
while (superclass)
{
if (superclass == [NSObject class])
{
[classList addObject:someClass];
break;
}
[excluded addObject:NSStringFromClass(superclass)];
superclass = class_getSuperclass(superclass);
}
} //remove all classes that have subclasses
for (Class someClass in excluded)
{
[classList removeObject:someClass];
} //free class list
free(classes);
} //check implementation cache first
NSString *selectorString = NSStringFromSelector(selector);
signature = signatureCache[selectorString];
if (!signature)
{
//find implementation
for (Class someClass in classList)
{
if ([someClass instancesRespondToSelector:selector])
{
signature = [someClass instanceMethodSignatureForSelector:selector];
break;
}
} //cache for next time
signatureCache[selectorString] = signature ?: [NSNull null];
}
else if ([signature isKindOfClass:[NSNull class]])
{
signature = nil;
}
}
return signature;
}
} - (void)forwardInvocation:(NSInvocation *)invocation
{
invocation.target = nil;
[invocation invoke];
} #endif @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

NSNull Crash处理 (NullSafe 的原理)的更多相关文章

  1. NullSafe 的原理

    摘要 NullSafe is a simple category on NSNull that returns nil for unrecognised messages instead of thr ...

  2. CoreData (表结构变化处理)

    引言: Core Data 是 iOS 3.0 以后引入的数据持久化解决方案,其原理是对SQLite的封装,是开发者不需要接触SQL语句,就可以对数据库进行的操作. 其编码方式和原理结构方面较为特殊, ...

  3. MySQL物理备份 xtrabackup

    MySQL 备份之 xtrabackup | innobackupex Xtrabackup 介绍 Xtrabackup 是一个对 InnoDB 做数据备份的工具,支持在线热备份(备份时不影响数据读写 ...

  4. InnoSQL HA Suite的实现原理与配置说明 InnoSQL的VSR功能Virtual Sync Replication MySQL 5.5版本引入了半同步复制(semi-sync replicaiton)的功能 MySQL 5.6支持了crash safe功能

    InnoSQL HA Suite的实现原理与配置说明  InnoSQL的VSR功能Virtual Sync Replication MySQL 5.5版本引入了半同步复制(semi-sync repl ...

  5. iOS App Crash原理分析

    预备知识:OS X系统分析 1.内核XNU是Darwin的核心,也是整个OS X的核心.XNU本身由以下几个组件构成: Mach微核心 BSD层 libKern I/O Kit 此外,内核是模块化的, ...

  6. Monkey捕获Crash原理

    Android的RuntimeInit.commonInit中会设置UncaughtHandler ActivityManagerService(AMS)在执行handleApplicationCra ...

  7. 字节Android Native Crash治理之Memory Corruption工具原理与实践

    作者:字节跳动终端技术--庞翔宇 内容摘要 ​ MemCorruption工具是字节跳动AppHealth (Client Infrastructure - AppHealth) 团队开发的一款用于定 ...

  8. iOS中常见 Crash 及解决方案

    来源:枫影JustinYan 链接:http://justinyan.me/post/1609 一.访问了一个已经被释放的对象 在不使用 ARC 的时候,内存要自己管理,这时重复或过早释放都有可能导致 ...

  9. JSPatch 实现原理详解

    原文地址https://github.com/bang590/JSPatch/wiki/JSPatch-%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E8%AF%A6%E8 ...

随机推荐

  1. [AHOI2004]数字迷阵

    嘟嘟嘟 应该算一道结论题吧. 首先很明显的一点,就是对于ai,j,我们只用求出ai,1就行了,剩下的就是斐波那契和矩阵快速幂的事. 至于如何求ai,1,用两种方法,但是哪一个我都不懂为啥,网上也没有解 ...

  2. tp5.0和tp3.2中前台模板IF标签和FOREACH的区别

    IF标签 tp3.2 <if condition="($name eq 1) OR ($name gt 100) "> value1 <elseif condit ...

  3. (第二场)A Run 【动态规划】

    链接:https://www.nowcoder.com/acm/contest/140/A 题目描述: White Cloud is exercising in the playground.Whit ...

  4. halcon 使用elliptic_axis计算规则矩形角度

    elliptic_axis算子是用于计算与Region等价椭圆的相关参数的,注意必须使用规则矩形!!!,不规则区域想其他办法比如模板匹配. elliptic_axis (Region, Ra, Rb, ...

  5. vue.js 组件监听

    一.在通过点击事件触发的子组件中: addCart(event) { if (!event._constructed) { return; } if (!this.food.count) { Vue. ...

  6. Windows平台JxCore打包

    1.下载JxCore https://raw.githubusercontent.com/jxcore/jxcore-release/master/0311/jx_win64v8.zip 2.解压并配 ...

  7. ON_COMMAND ON_MESSAGE ON_NOTIFY区别与联系

    ON_COMMAND是菜单和工具栏项处理消息的宏 ON_MESSAGE是处理自定义消息的宏 ON_NOTIFY 是控件向其父窗口发送消息处理的宏 对这几个消息的理解要先了解一下Window消息的背景. ...

  8. SQL中对连表查询的建议

    多表连查时,如果存在多个唯一键可以做关联,尽可能选择有意义的code或name,能不选择无意义的id或者uuid最好! 所以在存储的时候也是这样,并且从始至终保持一致性.这样既降低了维护和阅读的难度, ...

  9. 工具 | Axure基础操作 No.2

    不废话了,直接如之前一样上操作图才是正道. 1.设置文本类型为密码或者文件类型 可以在属性中也选择最大长度制定长度. 如果设置类型为文件,在浏览器中就会自动变成选择本地文件的按钮. 2.文本框提示文字 ...

  10. SpringMVC——笔记

    使用 @RequestMapping 映射请求 Spring MVC 使用@RequestMapping 注解为控制器指定可以处理那些URL请求. 在控制器的类定义及方法定义处都可以标注 @Reque ...