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) 交換兩個方法的實現

相關文章
相關標籤/搜索