美文网首页
runtime.h(二)

runtime.h(二)

作者: 想聽丿伱說衹愛我 | 来源:发表于2020-08-10 16:24 被阅读0次

    版本:iOS13.5

    runtime.h

    需要引入头文件#import <objc/runtime.h>
    runtime其他方法通道

    索引

    • 返回类的名称。
      class_getName
    • 检测类对象cls是否为元类。
      class_isMetaClass
    • 获取类对象cls的父类。
      class_getSuperclass
    • 返回类的版本号。
      class_getVersion
    • 设置类的版本号。
      class_setVersion
    • 返回类的大小。
      class_getInstanceSize
    • 返回类的实例变量name的Ivar描述。
      class_getInstanceVariable
    • 返回类变量name的Ivar描述。
      class_getClassVariable
    • 返回类cls的所有变量的Ivar描述的指针数组。
      class_copyIvarList
    • 返回类cls的实例方法name。
      class_getInstanceMethod
    • 返回类cls的类方法name。
      class_getClassMethod
    • 返回消息转发时调用的函数指针。
      class_getMethodImplementation
    • 返回消息转发时调用的函数指针。(ARM64不可用)
      class_getMethodImplementation_stret
    • 检测类的实例cls是否能响应选择器sel。
      class_respondsToSelector
    • 返回类cls的方法的指针数组。
      class_copyMethodList
    • 检测类cls是否符合协议protocol。
      class_conformsToProtocol
    • 返回类cls支持的协议的数组指针。
      class_copyProtocolList
    • 返回类cls的属性name。
      class_getProperty
    • 返回类cls的属性的数组指针。
      class_copyPropertyList
    • 返回类cls的Ivar布局的描述。
      class_getIvarLayout
    • 返回类cls的弱Ivar布局的描述。
      class_getWeakIvarLayout
    • 向类cls中添加新方法。
      class_addMethod
    • 替换类cls中方法的实现指针。
      class_replaceMethod
    • 给类cls添加新的实例变量。
      class_addIvar
    • 给类cls添加协议。
      class_addProtocol
    • 给类cls添加一个属性。
      class_addProperty
    • 替换类cls的属性name的attributes。
      class_replaceProperty
    • 为类cls设置Ivar布局。
      class_setIvarLayout
    • 为类cls设置弱Ivar布局。
      class_setWeakIvarLayout

    详解

    • 返回类的名称。
    const char * _Nonnull class_getName(Class _Nullable cls) 
    

    例子见class_getSuperclass

    • 检测类对象cls是否为元类。
    BOOL class_isMetaClass(Class _Nullable cls) 
    

    元类 类所属的类 弄懂OC中的类与元类

    例子见class_getSuperclass

    • 获取类对象cls的父类。
    Class _Nullable class_getSuperclass(Class _Nullable cls) 
    

    NSObject派生的类也可以通过superclass来获取父类

    例:
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        Class class6 = object_getClass(dict);
        const char *className = class_getName(class6);
        NSLog(@"%s", className);
        BOOL isMeta = class_isMetaClass(class6);
        NSLog(@"%@", @(isMeta));
        isMeta = class_isMetaClass(object_getClass(class6));
        NSLog(@"%@", @(isMeta));
        Class class7 = class_getSuperclass(class6);
        NSLog(@"%@", NSStringFromClass(class7));
    输出:
    __NSDictionaryM
    0
    1
    NSMutableDictionary
    
    • 返回类的版本号。
    int class_getVersion(Class _Nullable cls)
    

    例子见class_setVersion

    • 设置类的版本号。
    void class_setVersion(Class _Nullable cls, int version)
    

    NSObject派生的类可以使用setVersion(内部是通过class_setVersion实现的)来设置版本号。

    version 要设置的版本号

    例:
        int version = class_getVersion(object_getClass(self));
        NSLog(@"%d", version);
        class_setVersion(self.class, 10);
        version = class_getVersion(object_getClass(self));
        NSLog(@"%d", version);
        [object_getClass(self) setVersion:20];
        version = class_getVersion(object_getClass(self));
        NSLog(@"%d", version);
    输出:
    0
    10
    20
    
    • 返回类的大小。
    size_t class_getInstanceSize(Class _Nullable cls) 
    

    例子见class_getClassVariable

    • 返回类的实例变量name的Ivar描述。
    Ivar _Nullable class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
    

    name 实例变量的名称(为属性时为_title0,为全局变量时为aaa)
    例子见class_getClassVariable

    • 返回类变量name的Ivar描述。
    Ivar _Nullable class_getClassVariable(Class _Nullable cls, const char * _Nonnull name) 
    

    类变量指元类的变量

    name 类变量的名称

    例:
        size_t size = class_getInstanceSize(object_getClass(self));
        NSLog(@"size = %ld", size);
        self.title0  = @"title0";
        Ivar ivar1 = class_getInstanceVariable(object_getClass(self), "_title0");
        id title0 = object_getIvar(self, ivar1);
        aaa = @"bbb";
        Ivar ivar11 = class_getInstanceVariable(object_getClass(self), "aaa");
        id aaa = object_getIvar(self, ivar11);
        NSLog(@"%@ %@", title0, aaa);
        //好像UIViewController只有isa一个类变量
        Ivar ivar2 = class_getClassVariable(object_getClass(self), "isa");
        id isa = object_getIvar(self, ivar2);
        NSLog(@"%@", isa);
    输出:
    size = 864
    title0 bbb
    runtime
    
    • 返回类cls的所有变量的Ivar描述的指针数组。
    Ivar _Nonnull * _Nullable
    class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount) 
    

    不包括父类声明的任何实例变量。您必须使用free释放该数组。

    outCount int的指针 保存数组元素的数量

    例:
        unsigned int count;
        Ivar *ivars = class_copyIvarList(object_getClass(self), &count);
        NSLog(@"count = %d", count);
        for (NSInteger i = 0; i < count; i++) {
            Ivar ivar = ivars[i];
            const char *name = ivar_getName(ivar);
            NSLog(@"%s", name);
        }
        free(ivars);
    输出:aaa为全局变量 _title0为属性
    count = 2
    aaa
    _title0
    
    • 返回类cls的实例方法name。
    Method _Nullable class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
    

    class_getInstanceMethod会在父类中搜索该实例方法,而class_copyMethodList不会在父类搜索。

    name 方法选择器
    例子见class_getClassMethod

    • 返回类cls的类方法name。
    Method _Nullable class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
    

    class_getClassMethod会在父类中搜索该实例方法,而class_copyMethodList不会在父类搜索。

    name 方法选择器

    例:
        Method method = class_getInstanceMethod(object_getClass(self), @selector(instanceMethod));
        NSLog(@"%@", NSStringFromSelector(method_getName(method)));
        method = class_getClassMethod(object_getClass(self), @selector(classMethod));
        NSLog(@"%@", NSStringFromSelector(method_getName(method)));
    
    - (void)instanceMethod {
        
    }
    
    + (void)classMethod {
        
    }
    输出:
    instanceMethod
    classMethod
    
    • 返回消息转发时调用的函数指针。
    IMP _Nullable class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) 
    

    函数指针是运行时内部的函数,而不是实际的方法实现。
    当调用[对象 方法名]时,实现上是调用该函数指针。
    class_getMethodImplementation可能比method_getImplementation(class_getInstanceMethod(cls,name))更快。

    • 返回消息转发时调用的函数指针。(ARM64不可用)
    IMP _Nullable class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name)
    OBJC_ARM64_UNAVAILABLEARM64
    

    class_getMethodImplementation 相同,但多了OBJC_ARM64_UNAVAILABLEARM64 条件

    • 检测类的实例cls是否能响应选择器sel。
    BOOL class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel) 
    

    通常应使用NSObject 的类方法instancesRespondToSelector或实例方法respondsToSelector,而不要使用此函数。
    只能响应实例方法,而类方法则返回NO。若想响应类方法,只需将cls传入类的元类即可。

    BOOL 能响应时返回YES,否则返回NO。

    例:
        BOOL responds = class_respondsToSelector(object_getClass(self), @selector(instanceMethod));
        BOOL responds1 = class_respondsToSelector(object_getClass(self), @selector(classMethod));
        NSLog(@"%@ %@", @(responds), @(responds1));
        responds =  [object_getClass(self) instancesRespondToSelector:@selector(instanceMethod)];
        responds1 =  [object_getClass(self) instancesRespondToSelector:@selector(classMethod)];
        NSLog(@"%@ %@", @(responds), @(responds1));
        responds1 = class_respondsToSelector(object_getClass(object_getClass(self)), @selector(classMethod));
        NSLog(@"%@", @(responds1));
    - (void)instanceMethod {
        
    }
    
    + (void)classMethod {
        
    }
    输出:
    1 0
    1 0
    1
    
    • 返回类cls的方法的指针数组。
    Method _Nonnull * _Nullable 
    class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount) 
    

    指针数组不包括由父类实现的任何实例方法。您必须使用free 释放数组。
    若要获取类方法,cls需要传入类的元类,请使用class_copyMethodList(object_getClass(cls), &count)
    若要获取由父类实现的实例方法,请使用class_getInstanceMethod
    若要获取由父类实现的类方法,请使用class_getClassMethod

    outCount int的指针 保存数组元素的数量

    例:
        unsigned int methodCount = 0;
        //获取所有实例方法
        Method *methods = class_copyMethodList(object_getClass(self), &methodCount);
        NSLog(@"%d", methodCount);
        for (NSInteger i = 0; i < methodCount; i++) {
            Method method = methods[i];
            NSLog(@"%@", NSStringFromSelector(method_getName(method)));
        }
        //获取所有类方法
        methods = class_copyMethodList(object_getClass(object_getClass(self)), &methodCount);
        NSLog(@"%d", methodCount);
        for (NSInteger i = 0; i < methodCount; i++) {
            Method method = methods[i];
            NSLog(@"%@", NSStringFromSelector(method_getName(method)));
        }
        free(methods);
    
    - (void)instanceMethod {
        
    }
    
    + (void)classMethod {
        
    }
    输出:因为该类有一个属性title0 所有多了setTitle0:和title0
    5
    setTitle0:
    instanceMethod
    title0
    .cxx_destruct
    viewDidLoad
    1
    classMethod
    
    • 检测类cls是否符合协议protocol。
    BOOL class_conformsToProtocol(Class _Nullable cls, Protocol * _Nullable protocol) 
    

    通常使用NSObjectconformsToProtocol方法代替此函数。

    如果cls符合协议,则返回YES,否则为NO。

    例:

    @protocol runtimeProtocol <NSObject>
    
    - (void)test;
    
    @end
    
    @interface runtime : UIViewController <runtimeProtocol>
    
    @end
    
        BOOL protocol = class_conformsToProtocol(object_getClass(self), @protocol(runtimeProtocol));
        BOOL protocol1 = [self conformsToProtocol:@protocol(runtimeProtocol)];
        NSLog(@"%@ %@", @(protocol), @(protocol1));
    输出:
    1 1
    
    • 返回类cls支持的协议的数组指针。
    Protocol * __unsafe_unretained _Nonnull * _Nullable 
    class_copyProtocolList(Class _Nullable cls, unsigned int * _Nullable outCount)
    

    该数组不包括父类采用的任何协议。您必须使用free释放数组。

    outCount int的指针 保存数组元素的数量

    例:
        unsigned int protocolCounts = 0;
        Protocol * __unsafe_unretained *protocols = class_copyProtocolList(object_getClass(self), &protocolCounts);
        NSLog(@"protocolCounts = %d", protocolCounts);
        for (NSInteger i = 0; i < protocolCounts; i++) {
            Protocol *protocol = protocols[i];
            NSLog(@"%@", NSStringFromProtocol(protocol));
        }
    
    @interface runtime : UIViewController <runtimeProtocol>
    
    @end
    输出:
    protocolCounts = 1
    runtimeProtocol
    
    • 返回类cls的属性name。
    objc_property_t _Nullable
    class_getProperty(Class _Nullable cls, const char * _Nonnull name)
    

    name 属性名

    例:
        //获取名为title0的属性
        objc_property_t property = class_getProperty(object_getClass(self), "title0");
        NSLog(@"%s", property_getName(property));
    
    • 返回类cls的属性的数组指针。
    objc_property_t _Nonnull * _Nullable
    class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
    

    该数组不包括父类的任何属性。您必须使用free释放数组。

    outCount int的指针 保存数组元素的数量

    例:
        unsigned int propertyCounts = 0;
        objc_property_t *propertys = class_copyPropertyList(object_getClass(self), &propertyCounts);
        NSLog(@"propertyCounts = %d", propertyCounts);
        for (NSInteger i = 0; i < propertyCounts; i++) {
            objc_property_t property = propertys[i];
            NSLog(@"%s", property_getName(property));
        }
    输出:title0是自定义的 其他的是自带的
    propertyCounts = 5
    title0
    hash
    superclass
    escription
    debugDescription
    
    • 返回类cls的Ivar布局的描述。
    const uint8_t * _Nullable class_getIvarLayout(Class _Nullable cls)
    

    IvarLayout 保存着类中strong类型的变量信息 详情请见IvarLayout揭开Ivar Layout的秘密小节

    typedef unsigned char uint8_t;

    • 返回类cls的弱Ivar布局的描述。
    const uint8_t * _Nullable class_getWeakIvarLayout(Class _Nullable cls)
    

    weakIvarLayout 保存着类中weak类型的变量信息 详情请见IvarLayout揭开Ivar Layout的秘密小节

    • 向类cls中添加新方法。
    BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                    const char * _Nullable types) 
    

    class_addMethod将添加父类方法实现的重写,但不会替换此类中的现有方法实现。要更改现有的实现,请使用runtimemethod_setImplementation

    name 新方法的选择器
    imp 新方法的函数指针 可通过class_getMethodImplementation获取
    types 字符数组,描述新方法参数的类型。
    无参数无返回值传"v@:",int类型返回值、一个入参时传入"i@:@",如果有方法对应的Method,可以通过method_getTypeEncoding获取。具体各参数对应下见面。
    BOOL 如果成功添加了该方法,则返回YES,否则返回否。该类已经包含具有该名称的方法也会返回NO。
    例子见class_replaceMethod

    #define _C_ID       '@'
    #define _C_CLASS    '#'
    #define _C_SEL      ':'
    #define _C_CHR      'c'
    #define _C_UCHR     'C'
    #define _C_SHT      's'
    #define _C_USHT     'S'
    #define _C_INT      'i'
    #define _C_UINT     'I'
    #define _C_LNG      'l'
    #define _C_ULNG     'L'
    #define _C_LNG_LNG  'q'
    #define _C_ULNG_LNG 'Q'
    #define _C_FLT      'f'
    #define _C_DBL      'd'
    #define _C_BFLD     'b'
    #define _C_BOOL     'B'
    #define _C_VOID     'v'
    #define _C_UNDEF    '?'
    #define _C_PTR      '^'
    #define _C_CHARPTR  '*'
    #define _C_ATOM     '%'
    #define _C_ARY_B    '['
    #define _C_ARY_E    ']'
    #define _C_UNION_B  '('
    #define _C_UNION_E  ')'
    #define _C_STRUCT_B '{'
    #define _C_STRUCT_E '}'
    #define _C_VECTOR   '!'
    #define _C_CONST    'r'
    
    • 替换类cls中方法的实现指针。
    IMP _Nullable
    class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                        const char * _Nullable types) 
    

    若该类没有该名字的方法实现,则会新增,同class_addMethod
    若已存在该名字的方法实现,则会替换,同method_setImplementation

    name 原方法的选择器
    imp 新方法的函数指针 可通过class_getMethodImplementation获取
    IMP 返回之原方法的函数指针
    types 字符数组,描述新方法参数的类型。

    例:
        SEL sel = @selector(instanceMethod1);
        BOOL responds2 = class_respondsToSelector(object_getClass(self), sel);
        NSLog(@"%@", @(responds2));
        IMP imp = class_getMethodImplementation(object_getClass(self), sel);
        //新增方法instanceMethod1
        BOOL success = class_addMethod(object_getClass(self), sel, imp, "v@:");
        responds2 = class_respondsToSelector(object_getClass(self), sel);
        //返回1表示新增成功
        NSLog(@"%@ %@", @(success), @(responds2));
        
        SEL sel1 = @selector(instanceMethod);
        [self instanceMethod];
        IMP imp1 = class_getMethodImplementation(objc_getClass("ViewController"), @selector(test));
        //将instanceMethod替换成ViewController的test
        class_replaceMethod(object_getClass(self), sel1, imp1, "v@:");
        [self instanceMethod];
    
    - (void)instanceMethod {
        NSLog(@"instanceMethod");
    }
    在ViewController中有test方法
    - (void)test {
        NSLog(@"test");
    }
    输出:
    0
    1 1
    instanceMethod
    test
    
    • 给类cls添加新的实例变量。
    BOOL class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size, 
                  uint8_t alignment, const char * _Nullable types) 
    

    此函数只能在objc_allocateClassPair之后和objc_registerClassPair之前调用。不支持将实例变量添加到现有类。不支持将实例变量添加到元类。
    若想给现有类添加存取值的变量,可使用runtimeobjc_setAssociatedObjectobjc_getAssociatedObject间接添加。

    name 变量名
    size 变量大小sizeof(NSString *)
    alignment 变量的对齐方式 任何指针类型的变量,请传递log2(sizeof(NSString *))
    types 变量类型的字符 可通过@encode(NSString *)获取或在class_addMethod中查询
    BOOL 如果成功添加了实例变量,则返回YES,否则为否。若该类已经包含具有该名称的实例变量,也会返回NO。

    例:
        //实际上是不能给object_getClass(self)添加参数的,因为他是现有类,此处只是作为展示
        class_addIvar(object_getClass(self), "age", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
    
    • 给类cls添加协议。
    BOOL class_addProtocol(Class _Nullable cls, Protocol * _Nonnull protocol) 
    

    BOOL 如果成功添加了该协议,则返回YES,否则返回NO,若该类已符合该协议也返回NO。

    例:
    @protocol runtimeProtocol1 <NSObject>
    
    @optional
    - (void)test;
    
    @end
    
    @interface runtime : UIViewController <runtimeProtocol>
    //此处并未添加runtimeProtocol1协议
    
    @end
    
        BOOL protocolAdd = class_addProtocol(object_getClass(self), @protocol(runtimeProtocol1));
        BOOL protocolCan = class_conformsToProtocol(object_getClass(self), @protocol(runtimeProtocol1));
        NSLog(@"%@ %@", @(protocolAdd), @(protocolCan));
    输出:
    1 1
    
    • 给类cls添加一个属性。
    BOOL class_addProperty(Class _Nullable cls, const char * _Nonnull name,
          const objc_property_attribute_t * _Nullable attributes, unsigned int attributeCount)
    

    name 属性名
    attributes 属性的attribute数组 attribute是一个结构体
    可通过runtimeproperty_getAttributes获取已有属性的attributes

    typedef struct {
        //attribute的名字
        const char * _Nonnull name; 
        //attribute的值 通常为空
        const char * _Nonnull value; 
    } objc_property_attribute_t
    

    attributeCount attribute数组元素的数量
    BOOL 如果成功添加了该属性,则返回YES,否则返回否,若该类已经具有该属性,也返回NO。

    例:
        objc_property_t property1 = class_getProperty(object_getClass(self), "title0");
        const char *attributes = property_getAttributes(property1);
        //先获取已有属性的attributes
        NSLog(@"%s", attributes);
        objc_property_attribute_t attribute0 = {"T", [[NSString stringWithFormat:@"@\"%@\"", @"NSString"] UTF8String]};//属性的类型 T表示type
        objc_property_attribute_t attribute1 = {"&", ""};//&表示strong C表示copy N表示nonatomic 其他的请自行查看
        objc_property_attribute_t attribute2 = {"N", ""};
        //根据已有属性的attributes来创建新的属性attributes
        objc_property_attribute_t attribute3  = {"V", [[NSString stringWithFormat:@"_%@", @"newProperty"] UTF8String]};//属性的名字
        objc_property_attribute_t attrs[] = {attribute0, attribute1, attribute2, attribute3};
        BOOL addProperty = class_addProperty(object_getClass(self), "newProperty", attrs, 4);
        NSLog(@"%@", @(addProperty));
        //查看newProperty的attributes
        objc_property_t property2 = class_getProperty(object_getClass(self), "newProperty");
        attributes = property_getAttributes(property2);
        NSLog(@"%s", attributes);
    输出:
    T@"NSString",&,N,V_title0
    1
    T@"NSString",&,N,V_newProperty
    
    • 替换类cls的属性name的attributes。
    void class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
          const objc_property_attribute_t * _Nullable attributes, unsigned int attributeCount)
    

    参数与class_addProperty含义相同

    例:
        objc_property_t property2 = class_getProperty(object_getClass(self), "newProperty");
        attributes = property_getAttributes(property2);
        NSLog(@"%s", attributes);
        //更改属性的名字
        objc_property_attribute_t attribute4  = {"V", [[NSString stringWithFormat:@"_%@", @"newProperty1"] UTF8String]};
        objc_property_attribute_t attrs1[] = {attribute0, attribute1, attribute2, attribute4};
        class_replaceProperty(object_getClass(self), "newProperty", attrs1, 4);
        property2 = class_getProperty(object_getClass(self), "newProperty");
        attributes = property_getAttributes(property2);
        NSLog(@"%s", attributes);
    输出:属性名由newProperty变成了newProperty1
    T@"NSString",&,N,V_newProperty
    T@"NSString",&,N,V_newProperty1
    
    • 为类cls设置Ivar布局。
    void class_setIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
    

    IvarLayout详情可见class_getIvarLayout,不建议手动更改

    layout Ivar布局

    • 为类cls设置弱Ivar布局。
    void class_setWeakIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
    

    IvarLayout详情可见class_getWeakIvarLayout,不建议手动更改

    相关文章

      网友评论

          本文标题:runtime.h(二)

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