RunTime运行时机制

最近在使用Swift的过程当中,需要缓存数据,缓存数据的过程中,归档解档是我非常喜欢的一种方式。在OC的通过归档解档缓存模型数据中的数组的时候,有一个归档基类可以实现一键实现自定义对象的归档,虽然通过桥接可以实现OC和Swift的无缝结合,但是还是喜欢单独Swift的基类,所以研究一下运行时,自己写一个归档基类。

iOS开发中的运行时机制是底层的关于C的实现机制,OC代码编译的过程中先转换成C语言的runtime代码,然后汇编运行,OC的底层实现机制大多都是基于运行时的,比如:对象的创建、一个对象属性的内存分配、消息机制、KVC赋值等过程。

Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的优势在于:我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等。

这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。对于Objective-C来说,这个运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行。这个运行时系统即Objc Runtime。Objc Runtime其实是一个Runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。引用1

类的相关操作

类名

  • 获取类名: const char * class_getName ( Class cls );
  • 获取父类类型:Class class_getSuperclass ( Class cls );
  • 判断一个类是否是元类(meta class): BOOL class_isMetaClass ( Class cls );
  • 计算一个实例化之后的占用的内存空间:size_t class_getInstanceSize ( Class cls );

属性

在objc_class中,所有的成员变量、属性的信息是放在链表ivars中的。ivars是一个数组,数组中每个元素是指向Ivar(变量信息)的指针

  • 获取类中指定名称实例成员变量的信息(返回一个指向name属性的Ivar指针):
    Ivar class_getInstanceVariable ( Class cls, const char *name );

  • 获取类成员变量的信息(返回的列表不包含父类的成员变量和属性):
    Ivar class_getClassVariable ( Class cls, const char *name );

  • 添加成员变量:
    BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
  • 获取整个成员变量列表(返回一个指向成员变量信息的数组,必须使用free()来释放这个数组):
    Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );

方法

  • 添加方法: BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
  • 获取实例方法: Method class_getInstanceMethod ( Class cls, SEL name );
  • 获取类方法: Method class_getClassMethod ( Class cls, SEL name );
  • 获取所有方法的数组: Method * class_copyMethodList ( Class cls, unsigned int *outCount );
  • 替代方法的实现: IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );

  • 返回方法的具体实现:

    • IMP class_getMethodImplementation ( Class cls, SEL name );

    • IMP class_getMethodImplementation_stret ( Class cls, SEL name );

  • 类实例是否响应指定的selector: BOOL class_respondsToSelector ( Class cls, SEL sel );

Demo: 操作类的属性和方法

测试类: Person

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
@interface Person : NSObject
{
@public
int height;

@private
float weight;
}

@property (copy, nonatomic) NSString *name;

@property (copy, nonatomic) NSString *sex;

@property (strong, nonatomic) NSArray *data;

- (void)testMyClass;

- (void)method1;

- (void)method2;


#import "Person.h"

@interface Person ()
{
NSInteger value1;

NSString *user;
}

@property (strong, nonatomic) NSDate *birth;

-(void)method3:(NSInteger)arg1 arg2:(NSString *)arg2;

@end


@implementation Person

+ (void)classMethod{

NSLog(@"classMethod");
}

- (void)method1{

NSLog(@"method1");
}

- (void)method2{

NSLog(@"method2");
}


- (void)method3:(NSInteger)arg1 arg2:(NSString *)arg2{

NSLog(@"arg1:%zd,arg2:%@",arg1, arg2);
}

- (void)method4 {

NSLog(@"method4");
}
@end

测试方法:要导入头文件#import <objc/runtime.h>

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
Person *xm = [[Person alloc] init];

//********************类和属性操作************************
//获取当前类的类型;Class是一个结构体,存储一个类类型的信息
Class cls = xm.class;

//根据类的类型获取类名
NSLog(@"class name is : %s", class_getName(cls));

//根据类的类型获取类的父类类名
NSLog(@"class superClass is : %@", class_getSuperclass(cls));

//判断一个类是否是元类;元类(meta class):用来存储一个类的所有类方法
NSLog(@"class is meta class : %@", class_isMetaClass(cls) ? @"Yes" : @"No");

//获取一个类的元类
Class meta_cls = objc_getMetaClass(class_getName(cls));
NSLog(@"meta_class is meta class : %@", class_isMetaClass(meta_cls) ? @"Yes" : @"No");

//获取实例的大小
NSLog(@"person class size is : %zu", class_getInstanceSize(cls));

//获取成员变量

unsigned int count = 0; //成员变量的个数

Ivar *ivars = class_copyIvarList(cls, &count); // 获取所有的成员变量的数组

for (NSInteger i=0; i<count; i++) {

Ivar ivar = ivars[i];
NSLog(@"variable`s name is: %s index is :%zd", ivar_getName(ivar), i);
}

free(ivars);

//返回一个指向包含name指定的成员变量信息的objc_ivar结构体的指针(Ivar)
Ivar str = class_getInstanceVariable(cls, "_name");
if (str != NULL) {

NSLog(@"%s", ivar_getName(str));
}

//********************方法操作************************

//获取一个类的所有属性
count = 0;

Method *methods = class_copyMethodList(cls, &count);

for (NSInteger i=0; i<count; i++) {

Method method = methods[i];
NSLog(@"method`s name is : %s", method_getName(method));
}

free(methods);

//判断一个方法是否存在
Method method1 = class_getInstanceMethod(cls, @selector(method1));

if (method1 != NULL) {

NSLog(@"method1 is exist---%s", method_getName(method1));
}

//判断类有没有 method3:arg2: 方法
NSLog(@"My class is %@ response to selector", class_respondsToSelector(cls, @selector(method3:arg2:)) ? @"" : @"not");

//获取方法实现,没有实现会出错,坏指针访问
IMP imp = class_getMethodImplementation(cls, @selector(method1));

imp();

运行结果:

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
2016-01-26 14:03:28.422 运行时[2683:159792] class name is : Person
2016-01-26 14:03:28.423 运行时[2683:159792] class superClass is : NSObject
2016-01-26 14:03:28.424 运行时[2683:159792] class is meta class : No
2016-01-26 14:03:28.424 运行时[2683:159792] meta_class is meta class : Yes
2016-01-26 14:03:28.425 运行时[2683:159792] person class size is : 64
2016-01-26 14:03:28.425 运行时[2683:159792] variable`s name is: height index is :0
2016-01-26 14:03:28.425 运行时[2683:159792] variable`s name is: weight index is :1
2016-01-26 14:03:28.425 运行时[2683:159792] variable`s name is: value1 index is :2
2016-01-26 14:03:28.426 运行时[2683:159792] variable`s name is: user index is :3
2016-01-26 14:03:28.427 运行时[2683:159792] variable`s name is: _name index is :4
2016-01-26 14:03:28.427 运行时[2683:159792] variable`s name is: _sex index is :5
2016-01-26 14:03:28.428 运行时[2683:159792] variable`s name is: _data index is :6
2016-01-26 14:03:28.488 运行时[2683:159792] variable`s name is: _birth index is :7
2016-01-26 14:03:28.489 运行时[2683:159792] _name
2016-01-26 14:03:28.489 运行时[2683:159792] method`s name is : method1
2016-01-26 14:03:28.489 运行时[2683:159792] method`s name is : method2
2016-01-26 14:03:28.489 运行时[2683:159792] method`s name is : method3:arg2:
2016-01-26 14:03:28.489 运行时[2683:159792] method`s name is : method4
2016-01-26 14:03:28.489 运行时[2683:159792] method`s name is : testMyClass
2016-01-26 14:03:28.489 运行时[2683:159792] method`s name is : sex
2016-01-26 14:03:28.490 运行时[2683:159792] method`s name is : setSex:
2016-01-26 14:03:28.490 运行时[2683:159792] method`s name is : birth
2016-01-26 14:03:28.490 运行时[2683:159792] method`s name is : setBirth:
2016-01-26 14:03:28.491 运行时[2683:159792] method`s name is : .cxx_destruct
2016-01-26 14:03:28.491 运行时[2683:159792] method`s name is : name
2016-01-26 14:03:28.491 运行时[2683:159792] method`s name is : data
2016-01-26 14:03:28.491 运行时[2683:159792] method`s name is : setData:
2016-01-26 14:03:28.491 运行时[2683:159792] method`s name is : setName:
2016-01-26 14:03:28.491 运行时[2683:159792] method1 is exist---method1
2016-01-26 14:03:28.492 运行时[2683:159792] My class is response to selector
2016-01-26 14:03:28.492 运行时[2683:159792] method1