zoukankan      html  css  js  c++  java
  • Runtime-b

    感谢大神分享

    依旧是网上很多runtime的资料,依旧是看不懂,,,这里给大家转化一下runtime,使它由隐晦难懂变得通俗易懂。

    (虽然截图和语言组织的有些凌乱,但是大家还是一点一点的阅读下去吧,可以新建一个工程,跟着我写的一步一步的自己走一遍,会有帮助的。)

    关于runtime理论性的东西可以参考我们同事hah的初识runtime(我俩的文章名字居然差不多)

    本文参照:教你快速上手Runtime。谢谢该文作者峥吖峥老师。有兴趣的也可以去峥老师的博客看看去。

    另外哪里写的不对,可以给我留言,我会及时改进的,谢谢大家。

    在这里我们对runtime进行如下的介绍:

    1、runtime的简单介绍

    2、runtime的消息发送

    3、用runtime交换方法

    4、动态添加方法

    5、动态添加属性

    6、runtime替换KVC,进行字典转换

     


     

    下边开始我们的通俗易懂的路程:

    1、配置环境:新建一个工程,在appdelegate里面将viewcontroller设置为rootcontroller,方便查看打印信息。


     

    创建一个person类,继承自NSObject,下图的PersonViewController无用。

    然后再看一下目前的工程文件结构:


     

    按照网上教程我们开始runtime的各个方法的实现。

    2、发送消息。举例:实例方法和类方法的调用原理。

    我们建立一个继承于NSObject的Person


     

    在.m中分别实现实例方法和类方法:


     

    然后在viewcontroller加入头文件:#import "Person.h"、#import <objc/message.h>

    方法的调用有两种,调用实例方法和调用类方法。我们用runtime去观察这两种方法的实质。

    1、viewDidLoad里面实现Person的实例化,并用这个实例调用Person里面的实例方法;

    2、直接用Preson类名或类对象调用类方法。


     

    此时我们可以看到


     

    但是按照网上教程上图蓝色框框里面的也应该可以放开注释的,可是不知道为什么我的放开注释以后会爆红:


     

    不知道是为什么(如果大家谁知道怎么回事,还烦请留言告诉我,再次谢谢大家了),,,所以这里的runtime并不友好。

    哎,终于知道怎么回事了,还是同事hah帮忙解决的:头文件导入错了,应该是导入:#import <objc/runtime.h>


     

    导入#import<objc/messge.h>也可以调用objc_msgSend()这个方法,但是不能传参,而导入#import<objc/runtime.h>,会出现这个方法:objc_msgSend(id, SEL, ...),是可以传参数的.

    在这里有的网友提到另外一种解决该问题的方案:谢谢北京-吴露。方案是:引入头文件<objc/objc-runtime>,command+点击进入这个方法,我们会看到下边的


     

    也就是说这个头文件把runtime和message都包含了,不过建议还是单独引入,因为这两个头文件里面的方法很多都类似,容易混淆,区别在于方法是否可以传参数。就像上边我们举例的问题。

    打印结果:


     

    其实这里咱们可以看到网上教程的注释:不管是实例方法还是类方法,其本质就是让对象或者类对象发送消息,即我们在OC中调用的实例方法和类方法在runtime运行时的时候实际上是转化成C语言的发送消息的语句。

    按照网上教程说的:消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现


     

    其实说的就是:找到对应的实例方法或者类方法。

    最后,这里的runtime的使用并不实用

    3、交换方法。


     

    其实写完下边的代码,在通过理解,你会发现这中所谓的“交换方法”实际上就是系统方法的重写、扩展、再替换回去的过程。

    首先我们看一下demo里面viewcontroller的结构(同样的personviewcontroller无用,忽略掉,这里的<objc/message.h>最好是换成#import<objc/runtime.h>):


     

    然后我们看viewDidLoad里面,在这里我们没有做什么操作,只是命名了一个image:这里我们的图片是在工程文件中的,所以走到替换方法里面的时候会判断不为空,所以对应的打印信息“加载空的图片”就不会打印出来。


     

    按照网上教程我们进行步骤一的操作,写一个名字叫做+ (instancetype)imageWithName:(NSString*)name的类方法。这个方法是为了替换的时候准备的。


     

    然后我们进行步骤二,这个步骤二很关键,就是替换的核心所在。


     

    注意:两个方法。前面一个是准备好的方法,后边一个是系统的方法,要用第一个方法换掉第二个方法,这里需要注意替换顺序。

    当我们将viewDidLoad里面的image换成一个空的图片的时候


     

    我们看到打印信息


     

    是走了图片为空的if判断里面的。

    所以,这个方法可以看成是网络请求一张图片,如果请求失败,可以用默认的图片进行展示,就像下边的方法,网络请求失败的时候会展示一张默认图。


     

     

    当然,这里不是说runtime这么高大上的方法仅仅能实现这么low的功能,仅做示例而已。

    其实这个方法替换可以用在线下替换线上代码里面,“黑魔法”,我还不会,只是听说过。下边继续学习runtime其他的方法。

    4、动态添加方法。


     

    动态添加方法,咱们的person类.h里面用到了分类:


     

    在这里拓展一下类扩展的知识:类扩展与分类的区别。谢谢作者:Mitchell孟晨

    在这里大家是不是会想到我们的面试题中:


     

    有的还是用英语问的:


     

    类的作用。参考自category解析。如果大家对类别特别感兴趣可以参考深入解析OC中的Category


     

    当我们在类中添加@interface的时候:


     

    会出现上图中的3个。

    第一个就是类:继承自谁。


     

    第二个是分类,就是分类。


     

     

    类扩展与分类的区别中有举例说明:虽然我们在分类中声明属性不会报错,但是@property并没有自动为我们设置的属性生成set、get方法。

    第三个就是extension。类扩展。


     

    再看上述中有一句话是这样说的:


     

    类扩展的作用:


     

    person类.m中:


     

    viewcontroller里面:


     

    这里没有实现eat4方法,所以会在viewcontroller中的viewdidload里面调用eat4的时候,会调用这个方法处理,并且把对应的方法列表传过来。用来判断为实现的方法是不是我们想要动态添加的方法。

    这里注意的是,峥老师教程里面在调用class_addMethod(self,@selector(eat4), (IMP)eatt,"v@:");这个方法的时候,第三个参数前面并没有添加“(IMP)”,不添加这个IMP,会报黄:


     

    说的什么不清楚,意思就是叫你添加上IMP。

    拓展一下performSelector调用和直接调用的区别:


     

    准备好了,我们看打印结果:


     

    这里我们可以看到NSStringFromSelector(sel)调用的就是eat4。

    扯了这么多怎么用,到底这个动态添加方法到底是用来干什么的呢,看看众多网友的理解。翻了好多,就下边的两个解释比较靠谱,大家凑合着看吧:


     

    当然还有最一开始咱们引入动态添加的时候的目的:


     

    不忘初心,方得始终。

    5、动态添加属性。

    想知道原理的请移步OC Associated Objects实现原理

    原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。

    看结构,添加了一个NSObject的分类:


     

    分类的.h文件,将set方法和name外漏,方便在viewcontroller里面调用。


     

    分类的.m文件,set方法和name的实现。


     

    最后看viewcontroller里面,引入分类的头文件,然后实例化NSObject,调用objc1的name。


     

    最后的打印结果:


     

    tips:其实呢这个动态添加属性,是在哪里用到的呢?具体的我也说不上来。其实按照我的理解,咱们这里所谓的动态添加属性,是在分类上边添加的属性,那么我就想了,为什么我不能在类自己本身上边添加属性呢?对吧?为什么非要在分类上边添加呢,还整了个动态添加属性,麻烦不?!原因有两个:

    1)如果你的类自己本身里面的逻辑或者代码比较多,而需求是新加一个功能,那么就可以在分类上边添加,因为分类和继承还是有区别的。咱们这样理解啊:如果你用的是继承,那么在子类a里面新添加的属性和方法,在父类以及其他继承父类的子类b里面是不能用新添加的属性和方法的,(有点绕)。但是分类就行,你在分类里面新添加了属性和方法,那么在其他用到这个类自己的地方,是可以用新添加的属性和方法的。好像和公有性、私有性有点关系。

    2)给系统的类添加属性。像UIView、UIImageView、UILabel、UIButton等等系统的类,你觉得系统的类里面的属性不够用(当然,像我这种小白还是觉得够用的,关键不知道各个类里面除了经常用的还有啥,,,)就可以自己添加属性,当然了,在MJ和SDWebimage里面其实也能看到动态添加属性的影子,只不过是我们大多数人没有进入人家里面去看具体的代码。同事hah在runtime初识里面也提到了这个:


     

    6、字典转模型

    峥老师说的“字典转模型的方式一:KVC”这个就是咱们平时JSON解析的时候经常用到的,这里不作进一步解释,看看峥老师说的用runtime字典转模型的方式。


     

    没搞明白。。。

    先用KVC吧。

     

    总的来说,即便咱们知道了runtime是怎么用的,但是没有合适的环境使用runtime,也是白搭。所以,咱们现在能做的就是先了解runtime,随时记在心中,以后哪里能够用到的,可以尝试着用runtime的特性和上述几个方法来实现,甚至于用runtime解决bug。

    另外这里有很多关于runtime的集合,大家可以参考:runtime专题



    文/和珏猫(简书作者)
    原文链接:http://www.jianshu.com/p/4fd0ef1a144b
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
  • 相关阅读:
    第三节 单因素方差分析
    第四十一节 ORM介绍和用元类实现
    第四十节 通过type创建复杂的类,元类应用
    第二节 检验方法使用条件考察
    HDFS HA误删namenode后报错Nameservice testCluster has no SecondaryNameNode or High-Availability partner的恢复
    spark sql cache时发现的空字符串问题
    centos7环境下ELK部署之elasticsearch
    CDH升级 5.7.5 --> 5.13.3(tar包方式)
    CDH部署(以5.7.5为例)
    人生苦短,Let's Go
  • 原文地址:https://www.cnblogs.com/isItOk/p/5648268.html
Copyright © 2011-2022 走看看