列举一些常用的运行时api. 本文Demo

Demo类代码

Cat.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@protocol Cute
@optional
- (void)isCute;
@end
@protocol Movable
@optional
- (void)run;
@end
@interface Cat : NSObject<Movable>
@property (nonatomic, copy) NSString *name;
- (void)mew;
@end

Cat.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@implementation Cat {
NSColor *_color;
}
+ (void)mews {
NSLog(@"+++++喵+++++");
}
- (instancetype)init {
self = [super init];
if (self) {
_color = [NSColor blackColor];
}
return self;
}
- (void)mew {
NSLog(@"-----喵-----");
}
- (void)run {
NSLog(@"~~run~~");
}
- (NSArray *)method0:(NSArray *)agu0
agu1:(CGFloat)agu1
agu2:(NSObject *)agu2
agu3:(NSString *)agu3
agu4:(CGRect)agu4 {
NSLog(@"test method");
return @[];
}
- (void)method0 {
NSLog(@">>>>>>>>0:%s", __func__);
}
- (void)method1 {
NSLog(@">>>>>>>>1:%s", __func__);
}
- (void)method2 {
NSLog(@">>>>>>>>2:%s", __func__);
}
+ (void)catClassMethod {
NSLog(@"~~cat class method~~");
}
@end

Cat+YYAdd.h

1
2
3
4
@interface Cat (YYAdd)
@property (nonatomic, assign) float weight;
- (void)jump;
@end

Cat+YYAdd.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@implementation Cat (YYAdd)
- (void)setWeight:(float)weight {
objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_ASSIGN);
}
- (float)weight {
return [objc_getAssociatedObject(self, _cmd) floatValue];
}
- (void)jump {
NSLog(@"~~Cat jump~~");
}
@end

Runtime Demo

class部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// 获取类名
const char *className = class_getName(Cat.class);
NSLog(@"%@", [NSString stringWithUTF8String:className]);
// 结果: Cat
// 获取父类
Class superClass = class_getSuperclass(Cat.class);
NSLog(@"%@", superClass);
// 结果: NSObject
// 该函数的作用是获取类的实例所占用内存的大小
size_t instanceSizeOfClass = class_getInstanceSize(Cat.class);
NSLog(@"%zu", instanceSizeOfClass);
// 结果: 24
// 获取实例变量
Ivar ivar = class_getInstanceVariable(Cat.class, "_color");
NSLog(@"%@", [NSString stringWithUTF8String:ivar_getName(ivar)]);
// 结果: _color
// 获取属性
objc_property_t prop = class_getProperty(Cat.class, "name");
NSLog(@"%@", [NSString stringWithUTF8String:property_getName(prop)]);
// 结果: name
// 获取实例方法的实现, 并执行
IMP mewImp = class_getMethodImplementation(Cat.class, @selector(mew));
mewImp();
// 结果: -----喵-----
// 获取类的所有实例
unsigned int ivarListCount = 0;
Ivar *ivars = class_copyIvarList(Cat.class, &ivarListCount);
for (NSInteger i = 0; i < ivarListCount; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSLog(@"ivarList: %@", [NSString stringWithUTF8String:name]);
}
free(ivars);
// 结果:
ivarList: _color
ivarList: _name
// 获取类的所有属性
unsigned int propertyListCount = 0;
objc_property_t *props = class_copyPropertyList(Cat.class, &propertyListCount);
for (NSInteger i = 0; i < propertyListCount; i++) {
objc_property_t property = props[i];
const char *name = property_getName(property);
NSLog(@"propertyList: %@", [NSString stringWithUTF8String:name]);
}
free(props);
// 结果:
propertyList: weight
propertyList: name
// 由此可见, 运行时添加的关联属性, 没有生成实例
// 获取类的所有实例方法
unsigned int methodListCount = 0;
Method *methods = class_copyMethodList(Cat.class, &methodListCount);
for (NSInteger i = 0; i < methodListCount; i++) {
Method method = methods[i];
SEL name = method_getName(method);
NSLog(@"methodList: %@", NSStringFromSelector(name));
}
free(methods);
// 结果:
methodList: mew
methodList: method0:agu1:agu2:agu3:agu4:
methodList: method0
methodList: method1
methodList: method2
methodList: init
methodList: .cxx_destruct
methodList: name
methodList: setName:
methodList: run
methodList: jump
methodList: setWeight:
methodList: weight
// 获取类所遵守的协议
unsigned int protocolListCount = 0;
Protocol * __unsafe_unretained *protocols = class_copyProtocolList(Cat.class, &protocolListCount);
for (NSInteger i = 0; i < protocolListCount; i++) {
Protocol *protocal = protocols[i];
const char *name = protocol_getName(protocal);
NSLog(@"protocolList: %@", [NSString stringWithUTF8String:name]);
}
free(protocols);
// 结果: protocolList: Movable
// 获取类的某个类方法
Method classMethod = class_getClassMethod(Cat.class, NSSelectorFromString(@"catClassMethod"));
SEL classMethodName = method_getName(classMethod);
NSLog(@"clasMethod: %@", NSStringFromSelector(classMethodName));
IMP classMethodNameIMP = method_getImplementation(classMethod);
classMethodNameIMP();
// 结果: clasMethod: catClassMethod
// 获取类的元类
Class metaClass = objc_getMetaClass("Cat");
NSLog(@"meta class: %@", metaClass);
// 结果: meta class: Cat
// 从元类中获取所有类方法
unsigned int metaMethodListCount = 0;
Method *metaMethods = class_copyMethodList(metaClass, &metaMethodListCount);
for (NSInteger i = 0; i < metaMethodListCount; i++) {
Method method = metaMethods[i];
SEL name = method_getName(method);
NSLog(@"metaMethodList: %@", NSStringFromSelector(name));
}
free(metaMethods);
// 结果:
metaMethodList: mews
metaMethodList: catClassMethod
// 证明类方法的确存放在元类中

其他api

// 获取isa指向的Class
Class object_getClass(id obj)

// 设置isa指向的Class
Class object_setClass(id obj, Class cls)

// 判断一个OC对象是否为Class
BOOL object_isClass(id obj)

// 判断一个Class是否为元类
BOOL class_isMetaClass(Class cls)

object部分

1
2
3
4
5
6
7
8
9
10
11
12
Cat *cat = Cat.new;
cat.name = @"小麻烦";
cat.weight = 5.0;
NSLog(@"cat weight: %@", @(cat.weight)); // cat weight: 5
objc_removeAssociatedObjects(cat);
NSLog(@"cat weight after remove: %@", @(cat.weight)); // cat weight after remove: 0
Ivar ivar = class_getInstanceVariable(Cat.class, "_color");
NSLog(@"internal instance old value: %@", object_getIvar(cat, ivar)); // internal instance old value: Generic Gray Gamma 2.2 Profile colorspace 0 1
object_setIvar(cat, ivar, [NSColor orangeColor]);
NSLog(@"internal instance new value: %@", object_getIvar(cat, ivar)); // internal instance new value: sRGB IEC61966-2.1 colorspace 1 0.5 0 1

动态创建一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// C语言实现得getter
NSString *personNameGetter(id classInstance, SEL _cmd) {
Ivar ivar = class_getInstanceVariable([classInstance class], "_name");
return object_getIvar(classInstance, ivar);
}
// C语言实现得setter
void personNameSetter(id classInstance, SEL _cmd, NSString *newName) {
Ivar ivar = class_getInstanceVariable([classInstance class], "_name");
id oldName = object_getIvar(classInstance, ivar);
if (oldName != newName) object_setIvar(classInstance, ivar, [newName copy]);
}
//下面对应的编码值可以在官方文档里面找到
//编码值 含意
//c 代表char类型
//i 代表int类型
//s 代表short类型
//l 代表long类型,在64位处理器上也是按照32位处理
//q 代表long long类型
//C 代表unsigned char类型
//I 代表unsigned int类型
//S 代表unsigned short类型
//L 代表unsigned long类型
//Q 代表unsigned long long类型
//f 代表float类型
//d 代表double类型
//B 代表C++中的bool或者C99中的_Bool
//v 代表void类型
//* 代表char *类型
//@ 代表对象类型
//# 代表类对象 (Class)
//: 代表方法selector (SEL)
//[array type] 代表array
//{name=type…} 代表结构体
//(name=type…) 代表union
//bnum A bit field of num bits
//^type A pointer to type
//? An unknown type (among other things, this code is used for function pointers)
Class PersonClass = objc_allocateClassPair(NSObject.class, "Person", 0);
// 添加属性必须在objc_allocateClassPair和objc_registerClassPair之间
BOOL result = class_addIvar(PersonClass,
"_gender",
sizeof(NSString *),
log2(sizeof(NSString *)),
"@");
BOOL result2 = class_addIvar(PersonClass,
"_name",
sizeof(NSString *),
log2(sizeof(NSString *)),
"@");
// 也可以将 "@" 用@encode(NSString)表示
if (result && result2) {
objc_registerClassPair(PersonClass);
}
else {
objc_disposeClassPair(PersonClass);
}
// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
// 结合文档, 添加属性需以T开头, V结尾
objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = {"C", ""};
objc_property_attribute_t nonatomic = {"N", ""};
objc_property_attribute_t backingIvar = {"V", "_name"};
objc_property_attribute_t attrs[] = {type, ownership, nonatomic, backingIvar};
class_addProperty(PersonClass, "name", attrs, 4);
SEL getter = NSSelectorFromString(@"name");
SEL setter = NSSelectorFromString(@"setName:");
// 添加方法
class_addMethod(PersonClass, getter, (IMP)personNameGetter, "@@:"); // or use method_getTypeEncoding for last argument
class_addMethod(PersonClass, setter, (IMP)personNameSetter, "v@:@");
// 以下为测试该类
Ivar ivar = class_getInstanceVariable(PersonClass, "_gender");
NSLog(@"Person ivar:%@",[NSString stringWithUTF8String:ivar_getName(ivar)]);
// 结果: Person ivar:_gender
objc_property_t prop = class_getProperty(PersonClass, "name");
NSLog(@"Person property:%@",[NSString stringWithUTF8String:property_getName(prop)]);
// 结果: Person property:name
Method method = class_getInstanceMethod(PersonClass, NSSelectorFromString(@"setName:"));
NSLog(@"%@", NSStringFromSelector(method_getName(method)));
// 结果: setName:
id person = [PersonClass new];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[person performSelector:setter withObject:@"Phoenix"];
NSLog(@"person name get:%@", [person performSelector:getter withObject:nil]);
#pragma clang diagnostic pop
// 结果: person name get:Phoenix

方法部分

1
2
3
4
5
6
7
8
9
10
void newCatMethod() {
NSLog(@"~~汪~~");
}
void replaceMethod() {
class_replaceMethod(Cat.class, @selector(mew), (IMP)newCatMethod, NULL);
IMP mew = class_getMethodImplementation(Cat.class, @selector(mew));
mew();
// 结果: ~~汪~~
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
Method method = class_getInstanceMethod(Cat.class, @selector(method0:agu1:agu2:agu3:agu4:));
#pragma clang diagnostic pop
SEL methodName = method_getName(method);
NSLog(@"%@", NSStringFromSelector(methodName));
// 结果: method0:agu1:agu2:agu3:agu4:
IMP methodIMP = method_getImplementation(method);
#pragma unused(methodIMP)
const char *attrs = method_getTypeEncoding(method);
NSLog(@"%@", [NSString stringWithUTF8String:attrs]);
// 结果: @80@0:8@16d24@32@40{CGRect={CGPoint=dd}{CGSize=dd}}48
unsigned int count = method_getNumberOfArguments(method);
NSLog(@"%u", count);
// 结果: 7 因为还有self 和 _cmd
for (unsigned int i =0 ; i < count; i++) {
char result[1024] = {};
method_getArgumentType(method, i, result, 1024);
NSLog(@"类型是 %s", result);
}
char returnType[1024] = {};
method_getReturnType(method, returnType, 1024);
NSLog(@"return type:%s",returnType);
struct objc_method_description result7 = *method_getDescription(method);
NSLog(@"description ~%@ ~%@",NSStringFromSelector(result7.name),[NSString stringWithUTF8String:result7.types]);

其他API

char method_copyReturnType(Method m)
char
method_copyArgumentType(Method m, unsigned int index)

// 选择器相关
const char sel_getName(SEL sel)
SEL sel_registerName(const char
str)

// 用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)

交换方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
Method result0 = class_getInstanceMethod(Cat.class, @selector(method0));
Method result1 = class_getInstanceMethod(Cat.class, @selector(method1));
Method result2 = class_getInstanceMethod(Cat.class, @selector(method2));
method_setImplementation(result0, method_getImplementation(result1));
method_getImplementation(result0)();
method_exchangeImplementations(result0, result2);
method_getImplementation(result0)();
method_getImplementation(result2)();
#pragma clang diagnostic pop
// 结果:
>>>>>>>>1:-[Cat method1]
>>>>>>>>2:-[Cat method2]
>>>>>>>>1:-[Cat method1]