isa-swizzling 是什么鬼?
刚看到这个名字估计很多人有点熟悉,Method Swizzling对不对,不熟悉也没关系,去看看之前的一篇文章黑魔法之Method Swizzling吧。不过也可以根据名称猜测出来所谓的isa-swizzling就是讲isa进行替换了的技术。如果有小伙伴问我什么是isa的话,不好意思,保安,有人来捣乱,拖出去~(开玩笑,关于什么是isa的话,大家可以去看看网络上很多关于OC的对象模型的文章)。
那么,既然是isa替换,那主角当然就是isa啦。那么这个技术出现在什么场景呢?其实这个技术在官方文档中关于KVO的文档中有提到过, 里面说到了,KVO是通过isa-swizzling来实现的。
Automatic key-value observing is implemented using a technique called isa-swizzling.
The isa pointer, as the name suggests, points to the object’s class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.
You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.
最后一段中也说到了,永远不要用用isa来判断一个类的继承关系,而是应该用class方法来判断类的实例。
看着好像很厉害的技术耶~(双眼bling~bling~发光)。那么为了验证这个技术真正的实现了,也就是isa替换了,我们写了下面一段代码来验证。大家一起来观摩一下:
// 这里用来说明printInfo方法都做了说明
@implementation Person
- (void)printInfo {
NSLog(@"isa:%@, supper class:%@", NSStringFromClass(object_getClass(self)), class_getSuperclass(object_getClass(self)));
NSLog(@"self:%@, [self superclass]:%@", self, [self superclass]);
NSLog(@"age setter function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(setAge:)));
NSLog(@"name setter function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(setName:)));
NSLog(@"printInfo function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(printInfo)));
}
@end
// 既然是KVO嘛,那我们就来模拟一个虚假的KVO的情况,并且打印对应的信息
Person *person = [[Person alloc] init];
NSLog(@"Before add observer--------------------------------------------------------------------------");
[person printInfo];
[person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:&PrivateKVOContext];
NSLog(@"After add observer--------------------------------------------------------------------------");
[person printInfo];
[person removeObserver:self forKeyPath:@"age"];
NSLog(@"After remove observer--------------------------------------------------------------------------");
[person printInfo];
根据之前一篇历史冤案之isKindOfClass与isMemberOfClass中提到,object_getClass其实打印的就是isa指针。
http://www.nscookies.com/runtime-objectmodel/
其中第一个Person的实现中,我们打印了isa, supperclass, 两个方法的setter的函数指针以及printInfo的函数指针。而运行代码中分别在添加Observer前,添加Observer以及删除Observer后分别打印出该类的信息。那么输出的结果是什么呢?
Before add observer--------------------------------------------------------------------------
isa:Person, supper class:NSObject
self:<Person: 0x7ffe5aeeb340>, [self superclass]:NSObject
age setter function pointer:0x10fe03c40
name setter function pointer:0x10fe03be0
printInfo function pointer:0x10fe03a30
After add observer--------------------------------------------------------------------------
isa:NSKVONotifying_Person, supper class:Person
self:<Person: 0x7ffe5aeeb340>, [self superclass]:NSObject
age setter function pointer:0x10ff04c7f
name setter function pointer:0x10fe03be0
printInfo function pointer:0x10fe03a30
After remove observer--------------------------------------------------------------------------
isa:Person, supper class:NSObject
self:<Person: 0x7ffe5aeeb340>, [self superclass]:NSObject
age setter function pointer:0x10fe03c40
name setter function pointer:0x10fe03be0
printInfo function pointer:0x10fe03a30
我们可以分别对比一下添加Observer前以及添加Observer之后(同样也可以比较添加Observer之后与删除Observer之后),我们发现isa, 通过class_getSuperclass, 以及age的setter方法都发生了变化。而且我们发现其添加Observer之后的class_getSuperclass得到的结果竟然是Person, isa竟然是NSKVONotifying_Person。 What the hell?这是在逗我?
不对,看看文章的标题,其实在添加KVO之后,isa已经替换成了NSKVONotifying_Person,而根据class_getSuperclass得到的结果竟然是Person, 然后age是使我们KVO需要观察的属性,它的setter函数指针变了。而我们也知道,所谓的OC的消息机制是通过isa去查找实现的,那么我们现在可以进行大胆的猜想:
其实KVO的实现可能是:
- 添加Observer 
- 通过runtime偷偷实现了一个子类,并且以NSKVONotifying_+类名来命名 
- 将之前那个对象的isa指针指向了这个子类。 
- 重写了观察的对象setter方法,并且在重写的中添加了willChangeValueForKey:以及didChangeValueForKey: 
- 移除Observer 
- 只是简单的将其的isa指向原来的类对象中 
然后我们在分析一下, 在真正调用的setAge:的情况下, 根据消息机制我们知道它先通过isa找到对应对象的类, 也就是现在NSKVONotifying_Person, 然后再去找setAge:,由于NSKVONotifying_Person这个对象重写了这个方法, 那么就会直接取当前的实现, 也就是带有willChangeValueForKey:以及didChangeValueForKey:, 那么自然就实现了对KVO的实现了。
最后在看看一幅图吧,稍微总结一下isa-swizzling是搞什么东东:

isa-swizzling 是什么鬼?的更多相关文章
- iOS Objective-C对象模型及应用
		前言 原创文章,转载请注明出自唐巧的技术博客. 本文主要介绍Objective-C对象模型的实现细节,以及Objective-C语言对象模型中对isa swizzling和method swizzli ... 
- iOS多款源码分享
		iOS精选源码 列表联动,Linkage 电商商品详情 AxcUIKit-控件整合框架,快速简单的使用高级控件 GKNavigationBarViewController-导航栏联动 仿京东的加入购物 ... 
- Objective-C 对象和消息模型
		Objective-C 对象模型 首先要了解一下Objective-C中关于类和对象的定义,Cocoa中大部分对象都是NSObject的子类(NSProxy是一个例外),继承了NSObject的方法. ... 
- Runtime - 消息发送原理
		Runtime - 消息发送原理. Objective-C运行时的核心就在于消息分派器objc_msgSend,消息分派器把选择器映射为函数指针,并调用被引用的函数. 要想理解objc_msgSend ... 
- ObjC之RunTime(下)
		之前通过学习官方文档对runtime有了初步的认识,接下来就要研究学习runtime到底能用在哪些地方,能如何改进我们的程序. 本文也可以从icocoa浏览. Swizzling Swizzling可 ... 
- Java Objective-C AOP
		Java Use an AOP library or byte-code engineering (BCEL, cglib, asm, etc) to create a sub-class on th ... 
- 一些iOS面试基础题总结
		一些iOS面试基础题总结 目录 多线程 AutoLayout objc_msgSend Runtime 消息转发 Category NSObject 与 objc_class Runloop Auto ... 
- Objective-C 利用OC的消息机制,使用Method Swizzling进行方法修改
		功能:修改父类不可修改函数方法,函数方法交换 应用场景:假如我们使用的他人提供一个的framework,.m已被打包成二进制.a无法修改源码,只留下.h头文件,那假如代码中某个函数出现了问题可以通过这 ... 
- Method Swizzling 剖析
		一.背景介绍 关于Method Swizzling的文章一大堆,讲的非常好的也数不胜数.不过,很多人只是会用,知道一些注意点.深入一点问的话,估计就答得不好.归其原因就是对Method Swizzli ... 
随机推荐
- ThinkPHP中U方法与url的四种访问模式
			ThinkPHP中U方法的用处主要是完成对url地址的组装,在模板中使用U方法而不是固定写死URL地址的好处在于,一旦你的环境变化或者参数设置改变,你不需要更改模板中的任何代码.在模板中的调用格式需 ... 
- PHP的MySQL扩展:MySQL数据库概述
			来源:http://www.ido321.com/1023.html 一.SQL:结构化查询语言 SQL(Structured Query Language)是高级的非过程化变成语言,专门用于查询和修 ... 
- mybatis系列-11-一对多查询
			11.1 需求 查询订单及订单明细的信息. 11.2 sql语句 确定主查询表:订单表 确定关联查询表:订单明细表 在一对一查询基础上添加订单明细表关联即可. SELECT order ... 
- 百度,你家云管家能靠谱点不?替你脸红!Shame on you!
			此文已提交百度云问题反馈, 坐等答复. 笔者最近下载某24+G分卷压缩文件, 24+G啊, 足足要下将近7个小时.满心欢喜的下载完却尼玛发现解压出错, 有6个文件无法解压?wrong password ... 
- 从Count看Oracle执行计划的选择
			一. 前言 在调查一个性能问题的时候,一个同事问道,为什么数据库有些时候这么不聪明,明明表上有索引,但是在执行一个简单的count的时候居然全表扫描了!难道不知道走索引更快么? 试图从最简单的coun ... 
- Firefox 火狐网址生成二维码扩展推荐
			该扩展并未在火狐官方的扩展站中上线,在火狐中国的站点中也几乎很难找到,只是作为火狐中国版的一个集成扩展. 各位想要使用该扩展,但又不愿安装火狐中国版的的浏览迷可以通过以下地址独立安装该扩展. 扩展地址 ... 
- homework-03 图形化化最大子序列和
			你现在使用的代码规范是什么, 和上课前有什么改进? 我们一开始使用的是C++完成的相关程序.本次因为一些原因,改为C#进行编写.因为2013-10-21在VS2012中,所以所有的代码都已经被VS自 ... 
- Linux下文件的压缩与打包
			一.Linux下常见的文件压缩命令: 在Linux的环境中,压缩文件的扩展名大多是:『*.tar, *.tar.gz, *.tgz, *.gz, *.Z, *.bz2』,为什么会有这样的扩展名呢? 这 ... 
- CodeForces 682D  Alyona and Strings (四维DP)
			Alyona and Strings 题目链接: http://acm.hust.edu.cn/vjudge/contest/121333#problem/D Description After re ... 
- hashCode()和equals()的用法
			使用hashCode()和equals() hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法. hashCode()方法 ... 
