美文网首页
深入浅出Runtime (二) Runtime底层详解

深入浅出Runtime (二) Runtime底层详解

作者: 物非0人非 | 来源:发表于2021-08-04 10:49 被阅读0次

    Runtime的定义?

    为了更好的认识类是怎么工作的,我们将要将一段Object-C的代码用clang看下底层的C/C++的写法

    在Object-C中的NSObject对象中

    @interface NSObject <NSObject> {
          Class isa  OBJC_ISA_AVAILABILITY;
    }
    

    Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针

    /// An opaque type that represents an Objective-C class.
    typedef struct objc_class *Class;
    /// Represents an instance of a class.
    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    

    由此可见可以看到id是指向objc_object的一个指针
    objc_class结构体中的定义如下:

    struct objc_class {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    
    #if !__OBJC2__
        Class _Nullable super_class                              OBJC2_UNAVAILABLE;
        const char * _Nonnull name                               OBJC2_UNAVAILABLE;
        long version                                             OBJC2_UNAVAILABLE;
        long info                                                OBJC2_UNAVAILABLE;
        long instance_size                                       OBJC2_UNAVAILABLE;
        struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
        struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
        struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
        struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
    #endif
    
    } OBJC2_UNAVAILABLE;
    /* Use `Class` instead of `struct objc_class *` */
    

    runtime使用当中,我们经常需要用到的字段,它们的定义

    isaClass对象,指向objc_class结构体的指针,也就是这个Class的MetaClass(元类)
    类的实例对象的 isa 指向该类;该类的 isa 指向该类的MetaClassMetaCalssisa对象指向RootMetaCalss

    super_class Class对象指向父类对象

    • 如果该类的对象已经是RootClass,那么这个super_class指向nil
    • MetaCalssSuperClass指向父类的MetaCalss
    • MetaCalssRootMetaCalss,那么该MetaClassSuperClass指向该对象的RootClass

    ivars 类中所有 属性的列表,使用场景:我们在字典转换成模型的时候需要用到这个列表找到属性的名称,去取字典中的值,KVC赋值,或者直接Runtime赋值

    methodLists 类中 所有的方法的列表,使用场景:如在程序中写好方法,通过外部获取到方法名称字符串,然后通过这个字符串得到方法,从而达到外部控制App已知方法。

    cache 主要用于 缓存常用方法列表,每个类中有很多方法,我平时不用的方法也会在里面,每次运行一个方法,都要去 methodLists遍历得到方法,如果类的方法不多还行,但是基本的类中都会有很多方法,这样势必会影响程序的运行效率,所以 cache在这里就会被用上,当我们使用这个类的方法时先判断 cache是否为空,为空从 methodLists找到调用,并保存到 cache,不为空先从 cache中找方法,如果找不到在去 methodLists,这样提高了程序方法的运行效率。

    protocols故名思义,这个类中都遵守了 哪些协议,使用场景:判断类是否遵守了某个协议上

    在介绍runtime的时候,需要了解下类的本质。

    类底层代码、类的本质?

    为了更好的认识类是怎么工作的,我们将要将一段Object-C的代码用clang看下底层的C/C++的写法

    typedef enum : NSUInteger {
        ThisRPGGame = 0,
        ThisActionGame = 1,
        ThisBattleFlagGame = 2,
    } ThisGameType;
    
    @interface Game : NSObject
    @property (copy,nonatomic)NSString *Name;
    @property (assign,nonatomic)ThisGameType Type;
    @end
    
    @implementation Game
    @synthesize Name,Type;
    
    - (void)GiveThisGameName:(NSString *)name{
        Name = name;
    }
    
    - (void)GiveThisGameType:(ThisGameType)type{
        Type = type;
    }
    @end
    

    使用命令,在当前文件夹中会出现Game.cpp的文件

    # clang -rewrite-objc Game.m
    

    由于生成的文件很庞大,可以仔细去研读,受益匪浅

    /*
     * 顾名思义存放property的结构体
     * 当我们使用perproty的时候,会生成这样一个结构体
     * 具体存储的数据为 
     * 实际内容:"Name","T@\"NSString\",C,N,VName" 
     * 原型:@property (copy,nonatomic)NSString *Name;
     * 这个具体是怎么实现的,我会在后面继续深入研究,本文主要来理解runtime的理解
     **/
    struct _prop_t {
        const char *name;        //名字
        const char *attributes;  //属性
    };
    
    /*
     *类中方法的结构体,cmd和imp的关系是一一对应的关系
     *创建对象生成isa指针,指向这个对象的结构体时 
     *同时生成了一个表"Dispatch table"通过这个_cmd的编号找到对应方法
     *使用场景:
     *例如方法交换,方法判断。。。
     **/ 
    struct _objc_method {
        struct objc_selector * _cmd;   //SEL 对应着OC中的@selector()
        const char *method_type;       //方法的类型
        void  *_imp;                   //方法的地址
    }; 
    
    
    /*
     * method_list_t 结构体:
     * 原型:
     * - (void)GiveThisGameName:(NSString *)name;
     * 实际存储的方式:
     * {(struct objc_selector *)"GiveThisGameName:", "v24@0:8@16", (void *)_I_Game_GiveThisGameName_}
     * 其主要目的是存储一个数组,基本的数据类型是 _objc_method
     * 扩展:当然这其中有你的属性,自动生成的setter、getter方法
     **/
     
    static struct _method_list_t {
        unsigned int entsize;  // sizeof(struct _objc_method)
        unsigned int method_count;
        struct _objc_method method_list[6];
    }
    
    /*
     * 表示这个类中所遵守的协议对象
     * 使用场景:
     * 判断类是否遵守这个协议,从而动态添加、重写、交换某些方法,来达到某些目的
     * 
     **/
    
    struct _protocol_t {
        void * isa;  // NULL
        const char *protocol_name;
        const struct _protocol_list_t * protocol_list; // super protocols
        const struct method_list_t *instance_methods;  // 实例方法
        const struct method_list_t *class_methods;     //类方法
        const struct method_list_t *optionalInstanceMethods;  //可选的实例方法
        const struct method_list_t *optionalClassMethods;  //可选的类方法
        const struct _prop_list_t * properties;  //属性列表
        const unsigned int size;  // sizeof(struct _protocol_t)
        const unsigned int flags;  // = 0
        const char ** extendedMethodTypes;  //扩展的方法类型
    };
    
    /*
     * 类的变量的结构体
     * 原型:
     * NSString *Name;
     * 存储内容:
     * {(unsigned long int *)&OBJC_IVAR_$_Game$Name, "Name", "@\"NSString\"", 3, 8}
     * 根据存储内容可以大概了解这些属性的工作内容
     **/
    struct _ivar_t {
        unsigned long int *offset;  // pointer to ivar offset location
        const char *name;  //名字
        const char *type;  //属于什么变量
        unsigned int alignment; //未知
        unsigned int  size;    //大小
    };
    
    
    /*
     *  这个就是类中的各种方法、属性、等等信息
     *  底层也是一个结构体
     *  名称、方法列表、协议列表、变量列表、layout、properties。。
     *  
     **/
    struct _class_ro_t {
        unsigned int flags;
        unsigned int instanceStart;
        unsigned int instanceSize;
        unsigned int reserved;
        const unsigned char *ivarLayout;  //布局
        const char *name;  //名字
        const struct _method_list_t *baseMethods;//方法列表 
        const struct _objc_protocol_list *baseProtocols; //协议列表
        const struct _ivar_list_t *ivars;  //变量列表
        const unsigned char *weakIvarLayout;  //弱引用布局
        const struct _prop_list_t *properties;  //属性列表
    };
    
    /*
     * 类本身
     * oc在创建类的时候都会创建一个 _class_t的结构体
     * 我的理解是在runtime中的object-class结构体在底层就会变成_class_t结构体
     **/
    struct _class_t {
        struct _class_t *isa;  //元类的指针
        struct _class_t *superclass; //父类的指针
        void *cache;   //缓存
        void *vtable;  //表信息、未知
        struct _class_ro_t *ro;  //这个就是类中的各种方法、属性、等等信息
    };
    
    
    /*
     * 类扩展的结构体
     * 在OC中写的分类
     **/
    struct _category_t {
        const char *name;  //名称
        struct _class_t *cls;  //这个是哪个类的扩展
        const struct _method_list_t *instance_methods;  //实例方法列表
        const struct _method_list_t *class_methods;     //类方法列表
        const struct _protocol_list_t *protocols;       //协议列表
        const struct _prop_list_t *properties;          //属性列表
    };
    

    类就是多个结构体组合的一个集合体,类中的行为、习惯、属性抽象,按照机器能懂的数据存储到我们底层的结构体当中,在我们需要使用的时候直接获取使用。

    那么就开始研究一下,类是如何使用,类的基本使用过程以及过程中runtime所做的事情。

    类底层是如何调用方法?

    了解了类的组成,那么类是通过什么样的形式去获取方法属性并得到应用?
    在Object-C开发中我们经常会说到,对象调用方法,其本质就是想这个对象发送消息,为什么会有这么一说?下面我们来验证一下。
    Object-C代码

    int main(int argc, char * argv[]) {
    
        Game *game = [Game alloc];
        [game init];
        [game Play];
        return  0;
    }
    

    底层代码的实现

    int main(int argc, char * argv[]) {
    
        Game *game = ((Game *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Game"), sel_registerName("alloc"));
        game = ((Game *(*)(id, SEL))(void *)objc_msgSend)((id)game, sel_registerName("init"));
        ((void (*)(id, SEL))(void *)objc_msgSend)((id)game, sel_registerName("Play"));
        return 0;
    }
    

    代码中使用了

    objc_msgSend 消息发送
    objc_getClass 获取对象
    sel_registerName 获取方法的SEL

    因为目前重点是objc_msgSend,其他的Runtime的方法会在后面继续一一道来, So 一个对象调用其方法,在`=Object-C中就是向这个对象发送一条消息,消息的格式

    objc_msgSend("对象","SEL","参数"...)
    objc_msgSend( id self, SEL op, ... )
    

    相关文章

      网友评论

          本文标题:深入浅出Runtime (二) Runtime底层详解

          本文链接:https://www.haomeiwen.com/subject/uxyzmltx.html