runtime.h(二)
版本: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)
通常使用
NSObject
的conformsToProtocol
方法代替此函数。
如果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
将添加父类方法实现的重写,但不会替换此类中的现有方法实现。要更改现有的实现,请使用runtime的method_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
之前调用。不支持将实例变量添加到现有类。不支持将实例变量添加到元类。
若想给现有类添加存取值的变量,可使用runtime的objc_setAssociatedObject
和objc_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是一个结构体
可通过runtime的property_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
,不建议手动更改