18- Runtime 从使用出发学习(从使用到概念)

Posted on Posted in iOS, 全部文章

前话

这段时间学习 Runtime,网上看了一些文章,这里整理一下,给准备学 Runtime 的朋友一个参考.希望大家可以看完’前话’—–‘Demo 下载’小节,再去看提到的文章.

Runtime 中最重要的就是消息机制.

参考

如这里边很多文章的作者提到的那样, Runtime 文章网上很多,但不是每一篇我们新手都能看懂,有一些直接堆积概念,作为初学的我们,肯定满头雾水. 学习一个陌生的东西,我觉得我们学习的路径,最好是先对其的概念有个很简单浅显的理解,然后仿照写几个 Demo,消除陌生感后,我们再深入的了解一下实现机制和具体方法的含义.

我第一篇看的文章 《OC最实用的runtime总结,面试、工作你看我就足够了!》,这篇文章很浅显,很易懂.跟着写完里边所有提到的案例之后,发现 Runtime 并没有半年前在网上打算学习看的那篇冗余的文章的所伴随的满头雾水.基本上对 Runtime 的基本方法有了一个具体的了解.

从这里,我们总结出 Runtime 的一些基本的函数方法,如下

  • 获得某个类的类方法 Method class_getClassMethod(Class cls , SEL name)
  • 获得某个类的实例对象方法 Method class_getInstanceMethod(Class cls , SEL name)
  • 交换两个方法的实现 void method_exchangeImplementations(Method m1 , Method m2)

  • set方法,将值value 跟对象object 关联起来(将值value 存储到对象object 中) 参数 object:给哪个对象设置属性 参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key 可以是任何类型:double、int 等,建议用char 可以节省字节 参数 value:给属性设置的值 参数policy:存储策略 (assign 、copy 、 retain就是strong) void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)

  • 利用参数key 将对象object中存储的对应值取出来 id objc_getAssociatedObject(id object , const void *key) 获得某个类的所有成员变量(outCount 会返回成员变量的总数) 参数: 1、哪个类 2、放一个接收值的地址,用来存放属性的个数 3、返回值:存放所有获取到的属性,通过下面两个方法可以调出名字和类型 Ivar *class_copyIvarList(Class cls , unsigned int *outCount)

  • 获得成员变量的名字 const char *ivar_getName(Ivar v)

  • 获得成员变量的类型 const char *ivar_getTypeEndcoding(Ivar v)

  • 获取协议列表 protocol_getName

  • 获取属性列表 property_getName

文/滕先洪(简书作者) 原文链接:http://www.jianshu.com/p/ab966e8a82e2 著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

然后,在 Swift 第一届参会群里,看到正好有人讨论 Runtime,有人截图在百度搜索靠前的这篇文章 《Runtime 10种用法(没有比这更全的了)》,名字有点夸张,但是相信作者把文章中提到的几个篇文章都已经看了一遍.如果我们把这些文章看一遍的话,基本对 Runtime 能够有个大概的了解了.而且这篇文章中,作者对几篇文章做了一个简单的概括.

从这里我们得出, Runtime的简单实用场景:

  • 动态交换两个方法的实现(Swizzle 黑魔法)
  • 拦截交换系统自带的方法(Swizzle 黑魔法)
  • 动态给分类的成员变量和成员方法
  • 给分类增加属性
  • 获得一个类的所有成员变量
  • 获取一个类的类方法,对象方法
  • 字典转模型,MJExtesion和 YYModel的基本实现原理
  • 动态添加方法 performSelector
  • 实现NSCoding的自动归档和自动解档
  • 在方法上增加额外功能
  • 动态更改变量的值

《Runtime 10种用法(没有比这更全的了)》这篇文章里提到了上边《OC最实用的runtime总结,面试、工作你看我就足够了!》这篇文章,除了这篇文章,还有《让你快速上手Runtime》,小码哥袁铮写的.

《runtime详解》这篇文章除了我们上边提到的几个基本用法之外,我们主要在这篇文章中查看几个参数的概念:

  • objc_msgSend
  • SEL
  • id
  • Class
  • Method
  • IMP
  • Cache
  • Property

《详解runtime运行时机制 》其中也有对参数的解析(和上一篇有重复),另外这篇文章,适合我们在基本掌握了前边 Runtime 的使用方法之后,想要对很多概念更加深入了解的诉求.比如:Method和IMP

除了上述几篇文章,在方法替换的时候,为了比较方法替换两种写法的区别,我还阅读了《Objective-C的方法替换》这篇文章

Runtime消息机制

如同最开始上边说的那样,Runtime最重要的就是消息机制,了解消息机制,查看苹果开源的实现源码,会让我们更加了解苹果针对Object-C语言的用心和强大。这篇文章《runtime-消息机制》推荐给大家,期间有些地方我也没有看懂,作者自己也说了,为了写这篇文章,看苹果源码头都大了。我们又不是天才,怎么可能随随便便就懂得。好在有人给我们整理了这些,我们站在巨人的肩膀上,再看源码即使有不清楚的地方,也有参考和指导。
还有该作者的另一篇文章《runtime-属性与变量》,写的很用心。

实战

在实际项目过程中,我遇到下边情况,我们都可以用Runtime完成:

  • 实现万能控制器跳转《iOS 万能跳转界面方法 (runtime实用篇一))》
  • 还有就是检测用户上网时候准确使用了多少数据流量.这对于现在 QQ, 网易云音乐赠送用户数据流量的统计十分重要
  • 适配iOS时,我们有时候不同系统版本的手机的iOS所用到的图片素材不一样,也可以用运行时轻松完成.这个实例在提到的文章中还有我的Demo代码下载中都有写出.

其它

在阅读 Runtime文章的时候,我还对元类和超类的概念比较的模糊.超类其实就是我们在翻译的时候,对 super class 的另一种翻译法,我们一般叫他’父类’,这样也更加容易理解. 元类可以读《Objective-C 中的元类(meta class)是什么?》这篇文章.我们首先搞清楚对象和类的数据结构,更加有助于理解. 加单来说元类就是类的类,而元类的类叫做根元类.以此类推,所有的元类使用根元类作为他们的类,根元类的元类则就是它自己。也就是说基类的元类的isa指针指向他自己。

Demo 下载

我已经对上边的 Runtime 的大部分代码进行了梳理,需要的可以点过来Demo 链接

文章段落摘要

(如有侵权,请联系我,立刻删除)

1.. 还有一些 NSObject 的方法可以从 Runtime 系统中获取信息,允许对象进行自我检查。例如: – class方法返回对象的类; – isKindOfClass: 和 -isMemberOfClass: 方法检查对象是否存在于指定的类的继承体系中(是否是其子类或者父类或者当前类的成员变量); – respondsToSelector: 检查对象能否响应指定的消息; – conformsToProtocol:检查对象是否实现了指定协议类的方法; – methodForSelector: 返回指定方法实现的地址。 文/Ammar(简书作者)

原文链接:http://www.jianshu.com/p/1e06bfee99d0

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

2.. 值得注意的时,objc_class 中也有一个 isa 指针,这说明 Objc 类本身也是一个对象。为了处理类和对象的关系,Runtime 库创建了一种叫做 Meta Class(元类) 的东西,类对象所属的类就叫做元类。Meta Class 表述了类对象本身所具备的元数据。 我们所熟悉的类方法,就源自于 Meta Class。我们可以理解为类方法就是类对象的实例方法。每个类仅有一个类对象,而每个类对象仅有一个与之相关的元类。 当你发出一个类似 NSObject alloc 的消息时,实际上,这个消息被发送给了一个类对象(Class Object),这个类对象必须是一个元类的实例,而这个元类同时也是一个根元类(Root Meta Class)的实例。所有元类的 isa 指针最终都指向根元类。 所以当 [NSObject alloc] 这条消息发送给类对象的时候,运行时代码 objc_msgSend() 会去它元类中查找能够响应消息的方法实现,如果找到了,就会对这个类对象执行方法调用。 图 上图实现是 super_class 指针,虚线时 isa 指针。而根元类的父类是 NSObject ,isa 指向了自己。而 NSObject 没有父类。 最后 objc_class 中还有一个 objc_cache ,缓存,它的作用很重要,后面会提到。

文/Ammar(简书作者) 原文链接:http://www.jianshu.com/p/1e06bfee99d0

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

3.. 获取方法地址 NSObject 类中有一个实例方法:methodForSelector , 你可以用它来获取某个方法选择器对应的 IMP ,举个例子: void (setter)(id, SEL, BOOL); int i; setter = (void ()(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)]; for ( i = 0 ; i < 1000 ; i++ ) setter(targetList[i], @selector(setFilled:), YES);

当方法被当做函数调用时,两个隐藏参数也必须明确给出,上面的例子调用了1000次函数,你也可以尝试给 target 发送1000次 setFilled: 消息会花多久。 虽然可以更高效的调用方法,但是这种做法很少用,除非时需要持续大量重复调用某个方法的情况,才会选择使用以免消息发送泛滥。 注意: methodForSelector: 方法是由 Runtime 系统提供的,而不是 Objc 自身的特性

文/Ammar(简书作者) 原文链接:http://www.jianshu.com/p/1e06bfee99d0

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

4.. 动态绑定所做的,即是在实例所属类确定后,将某些属性和相应的方法绑定到实例上。这里所指的属性和方法当然包括了原来没有在类中实现的,而是在运行时才需要的新加入的实现。**在Cocoa层,我们一般向一个NSObject对象发送-respondsToSelector:或者-instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应的类的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,
**在此时有机会动态地向类或者实例添加新的方法,也即类的实现是可以动态绑定的。

文/Onevcat(简书作者) 原文链接:https://onevcat.com/2012/04/objective-c-runtime/

著作权归作者所有,转载请联系作者获得授权,并标注“作者”。

参考文章

  1. 《OC最实用的runtime总结,面试、工作你看我就足够了!》
  2. 《Objective-C的方法替换》
  3. 《Runtime 10种用法(没有比这更全的了)》
  4. 《IOS SEL (@selector) 原理及使用总结(一)》
  5. 《iOS 万能跳转界面方法 (runtime实用篇一))》
  6. 《详解runtime运行时机制 》
  7. 《runtime详解》
  8. 《Runtime 10种用法(没有比这更全的了)》
  9. 《让你快速上手Runtime》
  10. 《官方维护的 Demo》
  11. 《猫神提到的 Runtime》
  12. 《runtime-消息机制》
  13. 《runtime-属性与变量》

希望能和大家交流技术

我的博客地址: http://www.lilongcnc.cc/


发表评论

电子邮件地址不会被公开。 必填项已用*标注