Runtime整理

Classes

1
2
3
4
5
6
7
8
9
10
11

1.class_getName

作用:获得一个类的类名;

声明:const char * class_getName(Class cls)

参数:类对象(Class cls)

return: 返回一个char类型类对象的name, 如果参数是空,则返回
Nil;
1
2
3
4
5
6
7
8
9
10
11

2.class_getSuperclass

声明:Class class_getSuperclass(Class cls)

作用:获得一个类的父类

参数:类对象(Class cls);

return:返回父类类对象,如果参数是基类(如NSObject)或者Nil,返
回Nil;
1
2
3
4
5
6
7
8
9
10

3.class_setSuperclass

声明:Class class_setSuperclass(Class cls, Class newSuper)

作用:给传入的类设置父类;

参数:想要更改父类的类对象(Class cls), 传入的类对象(Class cls)的新的父类

return:返回传入对象的原来的父类.
1
2
3
4
5
6
7
8
9
4.class_isMetaClass

声明:BOOL class_isMetaClass(Class cls)

作用:判断一个对象是否是元类

参数:类对象

return: 若参数类对象是元类返回YES;若不是或参数类对象为Nil,返回Nil
1
2
3
4
5
6
7
8
9
5.class_getInstanceSize

声明:size_t class_getInstanceSize(Class cls)

作用:返回一个实例对象的大小 单位是byte

参数:类对象

return:返回类对象的大小,单位为byte.若参数为Nil,返回0
1
2
3
4
5
6
7
8
9
6.class_getInstanceVariable

声明:Ivar class_getInstanceVariable(Class cls, const char* name)

作用:获得传入的类对象的指定name的实例变量;

参数:类对象(Class cls), 传入类对象的实例变量的名称(const char* name)

return: 返回一个指针,指向传入类对象的指定name的实例变量.Ivar指针包含有变量的信息,名字,类型,及距离一个对象实例本身地址的偏移.
1
2
3
7.class_getClassVariable --- OC中好像没有类实例变量?

声明:Ivar class_getClassVariable(Class cls, const char* name)
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
8.class_addIvar

声明:BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types)

作用:给一个类添加一个新的实例变量

官方文档:This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

这个函数只能用在objc_allocateClassPair 和 before objc_registerClassPair之间,并且不能给一个已经存在的类添加实例变量;

The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.

这个类不能是元类,不支持给元类添加成员变量;

The instance variable's minimum alignment in bytes is 1<<align. The minimum alignment of an instance variable depends on the ivar's type and the machine architecture. For variables of any pointer type, pass log2(sizeof(pointer_type)).
这个实例变量的最小字节数是1<<align. 这上边写的什么玩意,特么看不懂.

参数: 1.想要动态添加新成员变量的类对象.
2.新的实例变量的name.
3.实例变量的大小
4.alignment
5.参考<a>https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html>类型编码</a>
参数3,4 可通过以下代码获得(NSGetSizeAndAlignment("*", &size, &alignment);) //"*" const char *types 见上述类型编码
NSUInteger size;
NSUInteger alignment;
NSGetSizeAndAlignment("*", &size, &alignment);
创建两个NSUInteger变量,传入地址.得到两个变量值.

return: 添加成功返回YES.失败NO.
1
2
3
4
5
6
7
8
9
9.class_copyIvarList

声明:Ivar * class_copyIvarList(Class cls, unsigned int *outCount)

作用:获取传入类的所有属性和实例变量,及实例变量总个数

参数:类对象, 传入无符号整形变量地址.返回实例变量总个数

return:返回一个指针数组,数组储存所有实例变量的Ivar指针(见6.return:)
1
2
3
4
5
6
7
8
9
10.class_getProperty

声明:objc_property_t class_getProperty(Class cls, const char *name)

作用:返回传入类的指定属性.

参数:类对象, 属性名

return:objc_property_t类型的指针

运行时常用函数

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
1> <objc/runtime.h>
* Method class_getClassMethod(Class cls,SEL name)
获得某个类的类方法

* Method class_getInstanceMethod(Class cls,SEL name)
获得某个类的对象方法

* void method_exchangeImplementations(Method m1, Method m2)
交换2个方法的实现(重点,iOS 中的黑魔法Swizzle)

* void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
将值value根对象object关联起来(将值value存贮到对象object中)
参数key: 将来可以通过key去除这个存储的值
参数policy: 存贮策略(assign,copy,retain)

* id objc_getAssociatedObject(id object,sonst void *key)
利用参数key将对象object中存储的对应值取出来

* Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
获得某各类的所有成员变量(outCount会返回所有成员变量的总数)

* const char *ivar_getName(Ivar v)
获得成员变量的名字

* const char *ivar_getTypeEncoding(Ivar v)
获得成员变量的类型

* void free(void *);
释放内存
(当C语言函数名中包含了copy,create,retai,new等词语,都需要在最后释放资源)



2> <objc/message.h>
* void objc_msgSend(void)
给某个对象发送某个消息

运行时常见应用

  • 动态添加对象的成员变量和成员方法
  • 动态交换两个方法的实现(特别是交换系统自带的方法)
  • 获得某个类的所有成员方法,所有成员变量

代码

动态交换两个方法的实现(特别是交换系统自带的方法)

1
2
3
4
5

//交换方法实现
Method m1 = class_getClassMethod([Person class], @selector(run));
Method m2 = class_getClassMethod([Person class], @selector(study));
method_exchangeImplementations(m1, m2);

-

交换方法实现,兼容版本具体应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "UIImage+Extension.h"
#import <objc/runtime.h>

@implementation UIImage (Extension)
//交换方法
+(void)load{
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(ac_imageNamed:));
method_exchangeImplementations(m1, m2);
}
+(UIImage *)ac_imageNamed:(NSString *)name{
double version = [[[UIDevice currentDevice] systemVersion]doubleValue];
if (version >= 7.0) {
name = [NSString stringWithFormat:@"%@_iOS7",name];
}
return [UIImage ac_imageNamed:name];
}
@end

-

给分类增加属性

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
#import <Foundation/Foundation.h>

@interface NSObject (Extension)
@property(nonatomic,copy)NSString *name;
@end


#import "NSObject+Extension.h"
#import <objc/runtime.h>


@implementation NSObject (Extension)


char NameKey;
-(void)setName:(NSString *)name{

objc_setAssociatedObject(self, &NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

-(NSString *)name{
return objc_getAssociatedObject(self, &NameKey);
}


@end

-

获取类的所有成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned int outCount = 0;
//获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList([Dog class], &outCount);
// 遍历所有的成员变量
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"%s %s",name,type);
}
//释放内存
free(ivars);

NSLog(@"%zd",outCount);

-

运行KVC时归档解档

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
#import "Dog.h"
#import <objc/runtime.h>
@interface Dog : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) double height;
@property (nonatomic, assign) double weight;
@property (nonatomic, assign) double aaa;
@property (nonatomic, assign) double bbb;
@property (nonatomic, assign) double ccc;
@end

#import "Dog.h"
#import <objc/runtime.h>
@implementation Dog

/*
这个数据的成员变量,不会被归档和解档
*/
-(NSArray *)ignoredNames{
return @[@"_aaa",@"_bbb",@"_ccc"];
}

/// 从文件中读取对象时会调用这个方法(说明需要读取的属性)
-(instancetype)initWithCoder:(NSCoder *)aDecoder{

if (self = [super init]) {

//用来存储成员变量的数量
unsigned int outCount = 0;

//获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList([self class], &outCount);

// 遍历所有的成员变量
for (int i = 0; i < outCount; i++) {
// 取出i位置对应的成员变量
Ivar ivar = ivars[i];

NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
//忽略解档对象
if ([[self ignoredNames] containsObject:key])
continue;
//获得key对应的值
id value = [aDecoder decodeObjectForKey:key];

//设置到成员变量上
[self setValue:value forKeyPath:key];
}

//释放内存
free(ivars);

}
return self;



}

/// 将对象写入文件时会调用这个方法(说明哪个属性需要存储)
-(void)encodeWithCoder:(NSCoder *)aCoder{

//用来存储成员变量的数量
unsigned int outCount = 0;
//获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList([self class], &outCount);
// 遍历所有的成员变量
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
//忽略归档对象
if ([[self ignoredNames] containsObject:key])
continue;
id value = [self valueForKeyPath:key];

[aCoder encodeObject:value forKey:key];
}

//释放内存
free(ivars);
}

//viewController点击方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

Dog *dog1 = [[Dog alloc]init];
dog1.age = 20;
dog1.height = 1.55;
dog1.name = @"wangcai";
dog1.weight = 50;
//将dog归档到桌面
[NSKeyedArchiver archiveRootObject:dog1 toFile:@"/Users/Chenzhaoshen/Desktop/dog.hm"];

//解档
Dog *dog = [NSKeyedUnarchiver unarchiveObjectWithFile:@"/Users/Chenzhaoshen/Desktop/dog.hm"];
NSLog(@"%@,%d,%f,%f",dog.name,dog.age,dog.height,dog.weight);

}

-

待续…