美文网首页
关于runtime的一些数据类型的分析

关于runtime的一些数据类型的分析

作者: philiha | 来源:发表于2017-09-18 22:01 被阅读0次

    总述

    运行时(runtime)是一种面向对象的编程语言的运行环境.
    运行时(runtime)是Objective-C的核心, Objective-C就是基于运行时(runtime)的.
    Objective-C是基于C语言加入了面向对象特性和消息转发机制的动态语言.
    Objective-C需要一个编译器,还需要Runtime系统来动态创建类和对象,进行消息发送和转发。
    Objective-C最主要的特点就是在程序运行时, 以发送消息的方式调用方法.
    C语言的函数调用方式是使用静态绑定(static binding).在编译期就能决定运行时所应调用的函数.
    在Objective-C中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数。
    头文件

     import <objc/runtime.h>
     import <objc/message.h>
    

    OC方法调用

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [self testDemo:nil];
    }
    
    - (void)testDemo:(id)param {
        NSLog(@"%s",__func__);
    }
    

    分析[self testDemo];

    1.self 叫做 接收者(receiver)
    2.testDemo 叫做 选择子(selector)
    3.选择子与参数合起来称为 消息(message)
    4.编译器看到此消息后,将其转换为一条标准的C语言函数调用
    5.所调用的函数是消息传递机制中的核心函数,叫做objc_msgSend()

    运行时(runtime)消息发送 == OC方法调用底层实现

    运行时(runtime)消息发送函数:OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
    OBJC2_UNAVAILABLE是一个Apple对Objc系统运行版本进行约束的宏定义,主要为了兼容非Objective-C 2.0的遗留版本

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 消息发送 : iOS8以后的特殊写法
        ((void(*)(id,SEL,id))objc_msgSend)(self,@selector(testDemo:),nil);
    }
    
    - (void)testDemo:(id)param {
        NSLog(@"%s",__func__);
    }
    

    数据类型分析

    SEL : 方法选择器(指向objc_selector结构体的指针)

    typedef struct objc_selector *SEL;

    1.可以通过Objc编译器命令@selector()或者Runtime系统的sel_registerName()函数来获取一个SEL类型的方法选择器.
    2.如果知道selector对应的方法名是什么,可以通过NSString* NSStringFromSelector(SEL aSelector)方法将SEL转化为OC字符串

    id : 对象(指向objc_object结构体的指针)

    // objc_object结构体
    struct objc_object {
        // id的成员 : isa
        Class isa  OBJC_ISA_AVAILABILITY;
    };
    // 指向objc_object结构体指针
    typedef struct objc_object *id;
    

    1.包含一个Class isa成员.
    2.根据isa指针就可以找到对象所属的类.

    Class : 对象所属的类(指向objc_class结构体的指针)<if见面自动加井号>

    typedef struct objc_class *Class;

    struct objc_class {
        Class isa  OBJC_ISA_AVAILABILITY;
    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;
    

    Method : 方法(指向objc_method结构体的指针)

    typedef struct objc_method *Method;
    struct objc_method {
        SEL method_name            OBJC2_UNAVAILABLE;
        char *method_types         OBJC2_UNAVAILABLE;
        IMP method_imp             OBJC2_UNAVAILABLE;
    }
    

    objc_method存储了方法名(method_name)、方法类型(method_types)和方法实现(method_imp)等信息.
    method_imp的数据类型是IMP,它是一个函数指针.

    IMP : 方法实现(指向方法实现的指针)

    #if !OBJC_OLD_DISPATCH_PROTOTYPES
    typedef void (*IMP)(void /* id, SEL, ... */ );
    #else
    typedef id (*IMP)(id, SEL, ...);
    #endif
    

    IMP本质上就是一个函数指针,指向方法的实现.
    当向某个对象发送一条信息时,可以由这个函数指针来指定方法的实现,它最终就会执行那段代码.

    Ivar : 实例变量(指向objc_ivar结构体的指针)

    typedef struct objc_ivar *Ivar;
    struct objc_ivar {
        char *ivar_name                             OBJC2_UNAVAILABLE;
        char *ivar_type                             OBJC2_UNAVAILABLE;
        int ivar_offset                             OBJC2_UNAVAILABLE;
    #ifdef __LP64__
        int space                                   OBJC2_UNAVAILABLE;
    #endif
    }
    

    Cache : 缓存(指向结构体objc_cache的指针)

    typedef struct objc_cache *Cache                OBJC2_UNAVAILABLE;
    struct objc_cache {
        unsigned int mask /* total = mask + 1 */    OBJC2_UNAVAILABLE;
        unsigned int occupied                       OBJC2_UNAVAILABLE;
        Method buckets[1]                           OBJC2_UNAVAILABLE;
    }
    

    其实就是一个存储Method的链表,主要是为了优化方法调用的性能.

    image.png

    类关系图

    image.png

    1.实例对象在运行时被表示成 objc_object 类型结构体,结构体内部有个isa指针指向 objc_class 结构体。
    2.objc_class 内部保存了类的变量和方法列表以及其他一些信息,并且还有一个isa指针。这个isa指针会指向 metaClass(元类),元类里保存了这个类的类方法列表。
    3.元类里也有一个isa指针,这个isa指针,指向的是根元类,根元类的isa指针指向自己

    objc_class中信息查看

    Class : 对象所属的类(指向objc_class结构体的指针)

    typedef struct objc_class *Class;

    struct objc_class {
        Class isa  OBJC_ISA_AVAILABILITY;
    #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;
    

    代码示例

    获取类名
    /**
     获取类名
    
     @param cls 要获取类名的类
     @return 类名
     */
    + (NSString *)getClassName:(Class)cls {
        // 获取类名(C语言类型)
        const char *cName = class_getName(cls);
        // OC类型类名
        NSString *className = [NSString stringWithUTF8String:cName];
    
        return className;
    }
    
    获取成员变量列表
    /**
     获取成员变量列表(带下划线的都获取)
    
     @param cls 要获取成员变量列表的类
     @return 成员变量数组(成员变量名字和类型组合的字典数组)
     */
    + (NSArray *)getIvarList:(Class)cls {
        // 成员变量个数
        unsigned int count;
        // 获取所有的成员变量
        Ivar *ivarList = class_copyIvarList(cls, &count);
    
        // 准备数组容器
        NSMutableArray *ivarArrM = [NSMutableArray arrayWithCapacity:count];
    
        // 遍历成员变量
        for (NSInteger i = 0; i < count; i++) {
            // 获取成员变量名字
            const char *ivarName = ivar_getName(ivarList[i]);
            // 获取成员变量类型
            const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
    
            // 成员变量名字和类型组合的字典容器
            NSMutableDictionary *ivarDictM = [NSMutableDictionary dictionary];
            ivarDictM[@"name"] = [NSString stringWithUTF8String:ivarName];
            ivarDictM[@"type"] = [NSString stringWithUTF8String:ivarType];
    
            // 添加到数组容器
            [ivarArrM addObject:ivarDictM];
        }
        free(ivarList);
        return ivarArrM.copy;
    }
    
    获取属性列表
    /**
     获取属性列表(有setter和getter方法的属性)
    
     @param cls 要获取属性列表的类
     @return 属性数组(属性名字和属性描述的字典数组)
     */
    + (NSArray *)getPropertyList:(Class)cls {
        // 成员属性个数
        unsigned int count;
        // 获取所有成员变量
        objc_property_t *propertyList = class_copyPropertyList(cls, &count);
    
        // 成员属性容器
        NSMutableArray *propertyArrM = [NSMutableArray arrayWithCapacity:count];
    
        // 遍历成员属性
        for (NSInteger i = 0; i < count; i++) {
            // 获取属性名字和属性的属性描述
            NSString *name = [NSString stringWithUTF8String:property_getName(property)];
            NSString *attr = [NSString stringWithUTF8String:property_getAttributes(property)];
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            dict[@"name"] = name;
            dict[@"attr"] = attr;
            // 添加到数组容器
            [propertyArrM addObject:[dict copy]];
        }
        free(propertyList);
        return propertyArrM.copy;
    }
    
    
    类属性中的属性示例

    @property (nonatomic,strong, setter=setPublicProperty:) NSArray *publicProperty01;
    property_getAttributes输出为:
    attr = "T@\"NSArray\",&,N,SsetPublicProperty:,V_publicProperty01"; name = publicProperty01;
    其中 attr 的解释为:

    • T 代表类型标识
    • @ 代表为对象类型
    • NSArray 表示其实际类型
    • & 代表 retain 强引用(copy 用 C, weak 用 W)
    • N 代表 nonatomic (代表 natomic)
    • SsetPublicProperty: 前面大写S代表指定了 setter,后面跟着代表具体方法
    • V_publicProperty01 V代表其对应的成员,后面为成员的名字
    获取方法列表
    /**
     获取类的实例方法 : 属性的setter和getter方法,对象方法,不包括类方法
    
     @param cls 要获取类的实例方法的类
     @return 方法数组
     */
    + (NSArray *)getMethodList:(Class)cls {
        // 实例方法个数
        unsigned int count;
        // 获取所有方法(不包括类方法)
        Method *methodList = class_copyMethodList(cls, &count);
    
        // 方法容器
        NSMutableArray *methodArrM = [NSMutableArray arrayWithCapacity:count];
    
        // 遍历所有方法
        for (NSInteger i = 0; i < count; i++) {
            // 获取数据
            NSString *name = [NSString stringWithString:NSStringFromSelector(method_getName(method))];
            NSString *type = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            dict[@"name"] = name;
            dict[@"type"] = type;
            // 添加到数组
            [methodArrM addObject:[dict copy]];
        }
    
        // 通过元类获取类方法
        Class metaCls = objc_getMetaClass(class_getName(cls));
        methods = class_copyMethodList(metaCls, &count);
        for (NSInteger i = 0; i < count; i++) {
            Method method = methods[i];
            // 获取数据
            NSString *name = [NSString stringWithString:NSStringFromSelector(method_getName(method))];
            NSString *type = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            dict[@"name"] = name;
            dict[@"type"] = type;
            // 添加到数组
            [arrayM addObject:[dict copy]];
        }
    
        free(methodList);
        return methodArrM.copy;
    }
    
    方法类型编码示例
    /// 公有方法2
    - (void)publicMethodd02:(NSString *)str append:(int)age;
    

    method_getTypeEncoding 的输出为:
    name = "publicMethodd02:append:"; type = "v28@0:8@16i24";
    其中type解释为:

    • v 代表返回值为void
    • 28 代表整个方法参数占位的总长度
    • @0 @代表对象,objc_msgSend 函数传入的第1个参数(self), 后面的0代表位置0开始
    • :8 :代表SEL,objc_msgSend 函数传入的第2个参数(self),后面的8代表位置8开始
    • @16 @代表第1个参数的类型为对象类型,后面的16代表位置8开始
    • i24 i代表第2个参数的类型为int类型,后面的24代表位置24开始
    获取协议列表
    /**
     获取类的协议列表
    
     @param cls 获取协议列表的类
     @return 协议数组
     */
    + (NSArray *)getProtocolList:(Class)cls {
    
        // 协议个数
        unsigned int count;
        // 获取协议列表
        Protocol * __unsafe_unretained *protocolList = class_copyProtocolList(cls, &count);
    
        // 协议容器
        NSMutableArray *protocolArrM = [NSMutableArray arrayWithCapacity:count];
    
        // 遍历协议列表
        for (NSInteger i = 0; i < count; i++) {
            // 获取协议名字
            const char *protocolName = protocol_getName(protocolList[i]);
            // 添加到协议容器
            [protocolArrM addObject:[NSString stringWithUTF8String:protocolName]];
        }
        free(protocolList);
        return protocolArrM.copy;
    }
    

    相关文章

      网友评论

          本文标题:关于runtime的一些数据类型的分析

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