runtime记录

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了runtime记录相关的知识,希望对你有一定的参考价值。

前言:

      最初对于runtime的了解其实只停留在,知道这是一组C的方法,知道消息机制中会把方法调用转成objc_msgSend(theObject,@selector(objectMethod))。随后有一个具体的了解得益于一次尝试,使用runtime解决按钮连续点击限制;这个例子网上有一堆,就不再赘述,但是不得不说,runtime的基本使用都很巧妙的包含其中。也因此,我对runtime的理解更深入了;随后看了runtime的官方文档,知道了原来方法还有很多,但至此,依然不能灵活的运用。真正让我震撼的,是在敲MJExtension的时候runtime的使用灵活而随性,在我看来已经是出神入化(也可能我还没有理解的那么深刻),确实从中学习到很多。

 

需要知道的概念:

isa指针:

       isa指针是每一个对象都会有的,指向这个对象所对应的类,在此要能够区分类和对象的不同;

SEL:

       SEL相当于对方法的进一步封装,也可以当做是一个描述,每一个方法都有一个与之对应的SEL类型的数据,根据一个SEL可以找到方法地址,进而调用方法;

       其类型定义为:typedef struct object_selector *SEL;

       相关使用:SEL sel_1 = @selector(myMethodName:); //把一个方法名包装为一个SEL对象

                     NSString *str = NSStringFromSelector(sel_1); //得到一个SEL对象的名字;

                     [object performSelector:sel_1]; //performSelector中其实一直都在使用SEL对象;

                     

Method:

      Method是在方法链表中的存在形式,其中有一个SEL类型的selector和一个IMP类型的address,具体来说,就是一个对象的isa指针,指向对象所对应的类结构,而类结构中的参数包括父类、类名、版本信息、类信息、实例大小、实例参数链表、方法链表、方法缓存、协议链表;在方法链表中存储的就是Method; 

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY; //isa指针

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE; //父类
    const char *name                                         OBJC2_UNAVAILABLE; //类名
    long version                                             OBJC2_UNAVAILABLE;//版本信息
    long info                                                OBJC2_UNAVAILABLE;//类信息
    long instance_size                                       OBJC2_UNAVAILABLE;//实例大小
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;//实例参数链表
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;//方法链表
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;//方法缓存
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;//协议链表
#endif

} OBJC2_UNAVAILABLE;

  相关方法:class_getInstanceMethod(Class cls, SEL name)  //返回SEL对应的Method对象

 

IMP:

       IMP是指implementation的缩写,是指向方法地址的指针,每一个方法都有一个IMP地址,要注意,并不是只有属性才有地址,方法也有;

       相关方法:method_getImplementation(Method m);  //runtime中的方法,返回该方法的IMP类型的方法实现地址;

                     [object methodForSelector:object_SEL];  //对象可以调用这个封装好的方法直接获得IMP指针;

        [class instanceMethodForSelector:object_SEL];  //上一个方法是实例方法,遍历实例的方法列表返回对应IMP,这个方法是一个类方法,区别在于遍历的是类的方法列表;其实具体实现是一样的;都是通过class_getInstanceMethod进行;

 

class cache:

分发表:

 

 

 

      下面列出一些基本使用;

 
1、动态操作属性、方法;
 
     属性:
 
       objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
 
             object:表示给那个对象添加属性;
             key:属性对应的key值,再添加之后使用这个key来获取属性;
      value: 属性值;
             policy:策略枚举;
       理解:这个方法可以在你的复制方法中使用,赋值的时候直接关联一个新的属性,使用runtime写在方法内部,但是从表面来看抽象出的方法依然只完成了对于属性赋值,只不过内部的实现不再是 class.aaa = value 这样的语句而已;同时,如果在添加过属性关联之后,想要断开某个属性的关联,直接value传nil即可;
 
 
        objc_getAssociatedObject(id object, const void *key)
 
              object: 要取的是哪个对象的属性;
              key:属性对应的key值;
        理解:同上,和普通取值有所不同,但抽象之后依然只是取值,值得注意的是,返回值为id类型;
 
 
        objc_removeAssociatedObjects(id object)
        
              object:断开所有属性关联的对象;
        理解:其实从方法名也可以理解,这个方法用于断开所有关联的属性,会把对象恢复到原始状态;
 
         runtime属性的总结:其实动态的操作属性,就是在进行关联,关联本身就是runtime实现的一部分;我不需要修改类的相关定义,在运行过程中,基于关键字的方式给这个对象增加存储空间;
 
 
 
     方法
 
        class_addMethod(Class cls,  SEL name, IMP imp, const char *types)
               
               cls:给哪个类添加方法;
               name: 原有方法的SEL,注意类型为SEL,你希望调用的方法名
               imp:新的方法的实现地址,当调用了这个name的方法时,你希望他执行哪一个方法地址;
               types:方法描述; 
        理解:向一个类增加一个方法实现,调用的时候其实还是老的方法名,但是执行的是新的方法实现过程,返回一个BOOL值,表示是否添加成功,其中最后一个参数表示方法的参数以及返回值描述;可使用method_getTypeEncoding(Method m)获得;其实这个方法包含了很多信息,有一个思考点,我们在代码中写了一个方法,但是内部是如何保存的呢?为什么就知道这个方法需要什么参数、什么样的返回值呢?其实这个方法已经比较隐含的表示了这个问题。
 

        class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

        理解:替换方法,参数类型同上;
 
        method_exchangeImplementations(Method m1, Method m2)
        理解:直接交换两个方法的实现地址,即,当你调用方法m1是,其实对应的是m2的智行过程,反之依然,简单粗暴而有效;
 
    runtime方法小结:方法的使用一般都放在category里,另外OC中runtime会自动加载load方法,但在Swift中没有实现load方法,可以使用初始化方法;
 
 
 
 
       转载请注明出处,有问题欢迎指出。
 
 

以上是关于runtime记录的主要内容,如果未能解决你的问题,请参考以下文章

discuz X3.1 源代码阅读,记录代码片段

提效小技巧——记录那些不常用的代码片段

用于从 cloudkit 检索单列的代码模式/片段

错误记录Flutter 混合开发获取 BinaryMessenger 报错 ( FlutterActivityAndFragmentDelegate.getFlutterEngine() )(代码片段

如何编译 dotnet/runtime 源代码

如何编译 dotnet/runtime 源代码 #yyds干货盘点#