runtime.h(二)

2020-08-10  本文已影响0人  想聽丿伱說衹愛我

版本:iOS13.5

runtime.h

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

索引

详解

const char * _Nonnull class_getName(Class _Nullable cls) 

例子见class_getSuperclass

BOOL class_isMetaClass(Class _Nullable cls) 

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

例子见class_getSuperclass

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

Ivar _Nullable class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)

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

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
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
Method _Nullable class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)

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

name 方法选择器
例子见class_getClassMethod

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))更快。

IMP _Nullable class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name)
OBJC_ARM64_UNAVAILABLEARM64

class_getMethodImplementation 相同,但多了OBJC_ARM64_UNAVAILABLEARM64 条件

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
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
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
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
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));
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
const uint8_t * _Nullable class_getIvarLayout(Class _Nullable cls)

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

typedef unsigned char uint8_t;

const uint8_t * _Nullable class_getWeakIvarLayout(Class _Nullable cls)

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

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'
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
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 *));
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
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
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
void class_setIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)

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

layout Ivar布局

void class_setWeakIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)

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

上一篇下一篇

猜你喜欢

热点阅读