Objective-C中runtime机制的应用

Objective-C中runtime机制的应用

1、初识runtime

        Objective-C是一种动态语言,所谓动态语言,是在程序执行时动态的肯定变量类型,执行变量类型对应的方法的。所以,在Object-C中经常使用字符串映射类的技巧来动态建立类对象。由于OC的动态语言特性,咱们能够经过一些手段,在程序运行时动态的更改对象的变量甚至方法,这就是咱们所说的runtime机制。程序员

2、你还有什么办法操做这样的变量么?

        首先,咱们先来看一个例子,这里有我建立的一个MyObject类:xcode

?安全

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
//.h===========================
@interface MyObject : NSObject
{
     @ private
     int  privateOne;
     NSString * privateTow;;
}
//=============================
//.m===========================
@interface MyObject()
{
     @ private
     NSString * privateThree;
}
@implementation MyObject
- (instancetype)init
{
     self = [super init];
     if  (self) {
         privateOne=1;
         privateTow=@ "Tow" ;
         privateThree=@ "Three" ;
     }
     return  self;
}
-(NSString *)description{
     return  [NSString stringWithFormat:@ "one=%d\ntow=%@\nthree=%@\n" ,privateOne,privateTow,privateThree];
}
@end
//=============================

这个类是至关的安全,首先,在头文件中没有提供任何的方法接口,咱们没有办法使用点语法作任何操做,privateOne和PrivateTow两个变量虽然声明在了头文件中,倒是私有类型的,经过指针的方式咱们虽然能够看到他们,却不能作任何读取修改的操做,xcode中的提示以下:函数

他会告诉咱们,这是一个私有的变量,咱们不能使用。对于privateThree,咱们更是一筹莫展,不只不能使用,咱们甚至都看不到它的存在。那么对于这种状况,你有什么办法操做这些变量么?对,是时候展示真正的技术了:runtime!ui

3、经过runtime获取对象的变量列表

        要操做对象的变量,咱们首先应该要捕获这些变量,让他们无处遁形。不管声明在头文件或是实现文件,不管类型是公开的仍是私有的,只要声明了这个变量,系统就会为其分配空间,咱们就能够经过runtime机制捕获到它,代码以下:spa

?.net

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
#import "ViewController.h"
#import "MyObject.h"
//包含runtime头文件
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController
- ( void )viewDidLoad {
     [super viewDidLoad];
     //咱们先声明一个unsigned int型的指针,并为其分配内存
     unsigned  int  * count =  malloc ( sizeof (unsigned  int ));
     //调用runtime的方法
     //Ivar:方法返回的对象内容对象,这里将返回一个Ivar类型的指针
     //class_copyIvarList方法能够捕获到类的全部变量,将变量的数量存在一个unsigned int的指针中
     Ivar * mem = class_copyIvarList([MyObject  class ], count);
     //进行遍历
     for  ( int  i=0; i< *count ; i++) {
         //经过移动指针进行遍历
         Ivar var = * (mem+i);
         //获取变量的名称
         const  char  * name = ivar_getName(var);
         //获取变量的类型
         const  char  * type = ivar_getTypeEncoding(var);
         NSLog(@ "%s:%s\n" ,name,type);
     }
     //释放内存
     free (count);
     //注意处理野指针
     count=nil;
}
- ( void )didReceiveMemoryWarning {
     [super didReceiveMemoryWarning];
     // Dispose of any resources that can be recreated.
}
 
@end

打印结果以下,其中i表示int型指针


是否是小吃惊了一下,不管变量在哪里,只要它在,就让它无处遁形。code

4、让我找到你,就让我改变你!

        仅仅可以得到变量的类型和名字或许并无什么卵用,没错,咱们获取变量的目的不是为了观赏,而是为了操做它,这对runtime来讲,也是小事一碟。代码以下:orm

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- ( void )viewDidLoad {
     [super viewDidLoad];
     //获取变量
     unsigned  int   count;
     Ivar * mem = class_copyIvarList([MyObject  class ],&count);
     //建立对象
     MyObject * obj = [[MyObject alloc]init];
     NSLog(@ "before runtime operate:%@" ,obj);
     //进行变量的设置
     object_setIvar(obj, mem[0],10);
     object_setIvar(obj, mem[1], @ "isTow" );
     object_setIvar(obj, mem[2], @ "isThree" );
     NSLog(@ "after runtime operate:%@" ,obj);
     
}

Tip:在修改int型变量的时候,你或许会遇到一个问题,ARC下,编译器不容许你将int类型的值赋值给id,在buildset中将Objective-C Automatic Reference Counting修改成No便可。

打印效果以下:


能够看到,那些看似很是安全的变量被咱们修改了。

5、让我看看你的方法吧

        变量经过runtime机制咱们能够取到和改变值,那么咱们再大胆一点,试试那些私有的方法,首先咱们在MyObject类中添加一些方法,咱们只实现,并不声明他们:

?

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
@interface MyObject()
{
     @ private
     NSString * privateThree;
}
@end
@implementation MyObject
- (instancetype)init
{
     self = [super init];
     if  (self) {
         privateOne=1;
         privateTow=@ "Tow" ;
         privateThree=@ "Three" ;
     }
     return  self;
}
-(NSString *)description{
     return  [NSString stringWithFormat:@ "one=%d\ntow=%@\nthree=%@\n" ,privateOne,privateTow,privateThree];
}
-(NSString *)method1{
     return  @ "method1" ;
}
-(NSString *)method2{
     return  @ "method2" ;
}

这样的方法咱们在外面是没法调用他们的,和操做变量的思路同样,咱们先要捕获这些方法:

?

1
2
3
4
5
6
7
8
     //获取全部成员方法
     Method * mem = class_copyMethodList([MyObject  class ], &count);
     //遍历
     for ( int  i=0;i<count;i++){
         SEL name = method_getName(mem[i]);
         NSString * method = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
         NSLog(@ "%@\n" ,method);
     }

打印以下:


获得了这些方法名,咱们大胆的调用便可:

?

1
2
     MyObject * obj = [[MyObject alloc]init];
     NSLog(@ "%@" ,[obj method1]);

Tip:这里编译器不会给咱们方法提示,放心大胆的调用便可。

6、动态的为类添加方法

        这个runtime机制最强大的部分要到了,试想,若是咱们能够动态的向类中添加方法,那将是一件多么使人激动的事情,注意,这里是动态的添加,和类别的最大不一样在于这种方式是运行时才决定是否添加方法的。

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- ( void )viewDidLoad {
     [super viewDidLoad];
     //添加一个新的方法,第三个参数是返回值的类型v是void,i是int,:是SEL,对象是@等
     class_addMethod([MyObject  class ], @selector(method3), (IMP)logHAHA,  "v" );
     
     unsigned  int  count = 0;
     Method * mem = class_copyMethodList([MyObject  class ], &count);
     for ( int  i=0;i<count;i++){
         SEL name = method_getName(mem[i]);
         NSString * method = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
         NSLog(@ "%@\n" ,method);
     }
     
     MyObject * obj = [[MyObject alloc]init];
     //运行这个方法
      [obj performSelector:@selector(method3)];
     
}
//方法的实现
void  logHAHA(){
     NSLog(@ "HAHA" );
}

运行结果以下:


从前五行能够看出,方法已经加进去了,从最后一行能够看出,执行没有问题。

7、作点小手脚

        程序员老是得寸进尺的,如今,咱们要作点事情,用咱们的函数替换掉类中的函数:

?

1
2
3
4
5
6
7
8
9
10
11
12
13
- ( void )viewDidLoad {
     [super viewDidLoad];
     MyObject * obj = [[MyObject alloc]init];
     //替换以前的方法
     NSLog(@ "%@" , [obj method1]);
     //替换
     class_replaceMethod([MyObject  class ], @selector(method1), (IMP)logHAHA,  "v" );
     [obj method1];
     
}
void  logHAHA(){
     NSLog(@ "HAHA" );
}

打印以下:


此次够cool吧,经过这个方法,咱们能够把系统的函数都搞乱套。固然,runtime还有许多很cool的方法:

id object_copy(id obj, size_t size)

拷贝一个对象


id object_dispose(id obj)

释放一个对象


const char *object_getClassName(id obj)

获取对象的类名

ive

void method_exchangeImplementations(Method m1, Method m2) 交换两个方法的实现

相关文章
相关标签/搜索