Objective-C是一门动态语言,一个函数是由一个selector(SEL),和一个implement(IML)组成的。
执行一个方法时如果系统找不到方法会给几次机会寻找方法,实在没有此方法就会抛出异常。

运行时查找函数的步骤

由图可见

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation 这两个函数是最后一个寻找IML的机会。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。

源码解读

#ifndef NULLSAFE_ENABLED
#define NULLSAFE_ENABLED 1
#endif // 忽略warning
// 三木运算符忽略中间一木导致的警告
#pragma GCC diagnostic ignored "-Wgnu-conditional-omitted-operand"

关闭警告

// 调用methodSignatureForSelector 方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
// 保持原子性,添加同步锁,防止被修改
@synchronized([self class])
{
}
}
 // 本类父类种寻找是否拥有此方法,拥有方法则直接返回 signature
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature)
{
}
return signature;
// 本类父类种寻找是否拥有此方法
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature)
{
// 方法列表
static NSMutableSet *classList = nil;
// 缓存方法字典
static NSMutableDictionary *signatureCache = nil;
if (signatureCache == nil)
{
classList = [[NSMutableSet alloc] init];
signatureCache = [[NSMutableDictionary alloc] init]; //get class list /*
分析:该函数的作用是获取已经注册的类,它需要传入两个参数,第一个参数 buffer :已分配好内存空间的数组,第二个参数 bufferCount :数组中可存放元素的个数,返回值是注册的类的总数。
当参数 bufferCount 值小于注册的类的总数时,获取到的是注册类的集合的任意子集
第一个参数传 NULL 时将会获取到当前注册的所有的类,此时可存放元素的个数为0,因此第二个参数可传0,返回值为当前注册的所有类的总数。
*/
// 获取项目中所有类的个数
int numClasses = objc_getClassList(NULL, ); // 调整一个classes的大小 = 获取一个class的 size * 所有的class
Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses); // 获取项目中class的个数
numClasses = objc_getClassList(classes, numClasses); // 初始化被排除的 NSMutableSet
NSMutableSet *excluded = [NSMutableSet set]; for (int i = ; i < numClasses; i++)
{
// 判断classes【i】 是否有superclass
Class someClass = classes[i];
Class superclass = class_getSuperclass(someClass); // 循环找出 someClass 的所有的superclass
while (superclass)
{
// 当superclass存在 判断是否等于 NSObject
if (superclass == [NSObject class])
{
// 等于 NSObject 加入 classList
[classList addObject:someClass];
break;
}
[excluded addObject:NSStringFromClass(superclass)];
superclass = class_getSuperclass(superclass);
}
} // 上面循环走完之后 查找到所有继承自NSObject的类 // 基于 NSObject 的类 中 删除 不基于 NSObject 类
for (Class someClass in excluded)
{
[classList removeObject:someClass];
} // 释放上面创建的 classes
free(classes);
}
经过上面代码获取项目中的类的列表和缓存
            // check implementation cache first
NSString *selectorString = NSStringFromSelector(selector);
signature = signatureCache[selectorString];
if (!signature)
{
for (Class someClass in classList)
{
// 判断 这个基于NSObject类的子类是否能够响应传入的方法
if ([someClass instancesRespondToSelector:selector])
{
// someClass类能够响应selector方法
// 返回NSMethodSignature对象,这个对象包含被标示的实例方法的描述。
signature = [someClass instanceMethodSignatureForSelector:selector];
break;
}
} // cache for next time
signatureCache[selectorString] = signature ?: [NSNull null];
}
else if ([signature isKindOfClass:[NSNull class]])
{
// 缓存是NSNull类型的话 将需要执行的方法置为nil
signature = nil;
}
// forwardInvocation:将选择器转发给一个真正实现了该消息的对象。
- (void)forwardInvocation:(NSInvocation *)invocation
{
// 将target = nil ,不发送
invocation.target = nil;
[invocation invoke];
}

总结:
当我们给一个NSNull对象发送消息的话,可能会崩溃(null是有内存的),而发送给nil的话,是不会崩溃的。

作者就是使用了这么一个原理,把发送给NSNull的而NSNull又无法处理的消息经过如下几步处理:

  1. 创建缓存,缓存项目中类的所有类名。
  2. 遍历缓存,寻找是否已经有可以执行此方法的类。
  3. 如果有的话,返回这个NSMethodSignature。
  4. 如果没有的话,返回nil,崩溃
  5. 如果有的话,[invocation invokeWithTarget:nil];将消息转发给nil。

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

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

NullSafe基于Runtime的深度解析的更多相关文章

  1. mybatis 3.x源码深度解析与最佳实践(最完整原创)

    mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...

  2. spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理

    @Configuration注解提供了全新的bean创建方式.最初spring通过xml配置文件初始化bean并完成依赖注入工作.从spring3.0开始,在spring framework模块中提供 ...

  3. Go netpoll I/O 多路复用构建原生网络模型之源码深度解析

    导言 Go 基于 I/O multiplexing 和 goroutine 构建了一个简洁而高性能的原生网络模型(基于 Go 的I/O 多路复用 netpoll),提供了 goroutine-per- ...

  4. 深度解析Maven

    此文来源于: https://www.cnblogs.com/hafiz/p/8119964.html 带你深度解析Maven   一.What`s Maven? Maven是基于项目对象模型(POM ...

  5. Spring源码深度解析之数据库连接JDBC

    Spring源码深度解析之数据库连接JDBC JDBC(Java Data Base Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供 ...

  6. [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析

    [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...

  7. Deep Learning模型之:CNN卷积神经网络(一)深度解析CNN

    http://m.blog.csdn.net/blog/wu010555688/24487301 本文整理了网上几位大牛的博客,详细地讲解了CNN的基础结构与核心思想,欢迎交流. [1]Deep le ...

  8. (转载)(收藏)OceanBase深度解析

    一.OceanBase不需要高可靠服务器和高端存储 OceanBase是关系型数据库,包含内核+OceanBase云平台(OCP).与传统关系型数据库相比,最大的不同点, 是OceanBase是分布式 ...

  9. Kafka深度解析

    本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/2015/01/02/Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅 ...

随机推荐

  1. go基础编程 day-2

    Go的常亮 关键字: Const Go常亮的多个定义: // 定义常亮的关键字 const // 定义多个常亮 const( PI = 3.14 const1 = " const2 = co ...

  2. JAVA描述的简单ORM框架

    抽了点时间自己写了个ORM,主要是为了复习JAVA泛型,映射,注解方面的知识.如需代码,可前往:https://github.com/m2492565210/java_orm自行下载 框架的类结构如下 ...

  3. 【ASP.NET MVC 学习笔记】- 09 Area的使用

    本文参考:http://www.cnblogs.com/willick/p/3331519.html 1.ASP.NET MVC允许使用 Area(区域)来组织Web应用程序,这对于大的工程非常有用, ...

  4. OOAD-设计模式(四)结构型模式之适配器、装饰器、代理模式

    前言 前面我们学习了创建型设计模式,其中有5中,个人感觉比较重要的是工厂方法模式.单例模式.原型模式.接下来我将分享的是结构型模式! 一.适配器模式 1.1.适配器模式概述 适配器模式(Adapter ...

  5. 博客迁移至 http://www.loveli.site

    对于博客园的Markdow 支持太过...,你懂的,  以后博客迁移至:http://www.loveli.site

  6. getSystemService详解

     android的后台运行在很多service,它们在系统启动时被SystemServer开启,支持系统的正常工作,比如MountService监听是否有SD卡安装及移除,ClipboardServi ...

  7. BZOJ-2463

    2463: [中山市选2009]谁能赢呢? Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2321  Solved: 1711[Submit][Sta ...

  8. 0_Simple__simpleAssert + 0_Simple__simpleAssert_nvrtc

    在核函数中使用强制终止函数 assert().并且在静态代码和运行时编译两种条件下使用. ▶ 源代码:静态使用 #include <windows.h> #include <stdi ...

  9. Python通过跳板机链接MySQL的一种方法

  10. 如何优雅的设计 React 组件

    作者:晓冬 本文原创,转载请注明作者及出处 如今的 Web 前端已被 React.Vue 和 Angular 三分天下,一统江山十几年的 jQuery 显然已经很难满足现在的开发模式.那么,为什么大家 ...