Objective-C中runtime机制的应用

Objective-C中runtime机制的应用

一、初识runtime

        Objective-C是一种动态语言,所谓动态语言,是在程序执行时动态的确定变量类型,执行变量类型对应的方法的。因此,在Object-C中常用字符串映射类的技巧来动态创建类对象。因为OC的动态语言特性,我们可以通过一些手段,在程序运行时动态的更改对象的变量甚至方法,这就是我们所说的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

27

28

29

30

31

32

//.h===========================

@interface MyObject : NSObject

{

    @private

    int privateOne;

    NSString * privateTow;;

}

@end

//=============================

//.m===========================

@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];

}

@end

//=============================

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

他会告诉我们,这是一个私有的变量,我们不能使用。对于privateThree,我们更是束手无策,不仅不能使用,我们甚至都看不到它的存在。那么对于这种情况,你有什么办法操作这些变量么?对,是时候展现真正的技术了:runtime!

三、通过runtime获取对象的变量列表

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

?


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型:

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

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

        仅仅能够获得变量的类型和名字或许并没有什么卵用,没错,我们获取变量的目的不是为了观赏,而是为了操作它,这对runtime来说,也是小事一碟。代码如下:

?


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即可。

打印效果如下:

可以看到,那些看似非常安全的变量被我们修改了。

五、让我看看你的方法吧

        变量通过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:这里编译器不会给我们方法提示,放心大胆的调用即可。

六、动态的为类添加方法

        这个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");

}

运行结果如下:

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

七、做点小手脚

        程序员总是得寸进尺的,现在,我们要做点事情,用我们的函数替换掉类中的函数:

?


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) 
交换两个方法的实现

时间: 2024-12-02 05:57:31

Objective-C中runtime机制的应用的相关文章

总结iOS中runtime的使用_IOS

做iOS的朋友都知道或听说runtime,这个东西很像java的反射机制,但功能远胜于java的反射.通过runtime我们可以动态的向一个类中添加属性.成员变量.方法,以及对其进行读写访问. 一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候 的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数. 对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称 找到对应的

浅析ASP.NET FORUMS中缓存机制的应用

asp.net|缓存 浅析ASP.NET FORUMS中缓存机制的应用 在ASP.NET中有三种缓存机制,我在这里主要提到的是ASP.NET FORUMS中使用的缓存API Cache对象Cache对象可以说在ASP.NET FORUMS中无所不在,以SiteUrls()类为例在构造函数中有如下代码if (HttpRuntime.Cache[cacheKey] == null) { . .System.Web.Caching.CacheDependency dep = new System.We

iOS中RunLoop机制浅探

iOS中RunLoop机制浅探 一.浅识RunLoop         RunLoop这个家伙在iOS开发中,我们一直在用,却从未注意过他,甚至都不从见过他的面孔,那个这个神秘的家伙究竟是做什么的?首先,我们先来观察一下我们的程序运行机制.         无论是面向对象的语言或是面向过程的语言,代码的执行终究是面向过程的.线程也一样,一个线程从开始代码执行,到结束代码销毁.就像HELLO WORLD程序,打印出字符串后程序就结束了,那么,我们的app是如何实现如下这样的机制的呢:app从运行开

Android中Handler机制问题

问题描述 Android中Handler机制问题 android中我们常通过handler来进行线程间的通信,里面通过Looper来进行消息管理.线程间的通信都可以自己来实现,Handler的优势在哪线程间通信 解决方案 Android中的Handler机制(一)android handler机制Android Handler机制 解决方案二: 最大优势是,Handler运行在主线程中(UI线程中),可以让子线程通过handler更新UI操作 解决方案三: handler主要优势就是线程通讯呀,

javasejava...-javase中反射机制原理

问题描述 javase中反射机制原理 基础班结课了,最后讲的反射机制,感觉晕了.请问大神们它的原理和作用是什么?谢谢啦. 解决方案 原理,Java和C++不同,在编译的时候把对象的字段.方法.方法的参数等描述信息(元数据,metadata)也写入了程序中. 因此java可以在运行的时候检索这些信息. 作用,运行时创建一个对象,调用方法,这个很适合插件开发,因为插件是在写了主程序之后开发的,只能动态调用 运行时类型查询,比如eclipse可以给你函数参数和成员提示,这个就是反射实现的. 解决方案二

Discuz!X中SESSION机制实例详解_php实例

本文实例讲述了Discuz!X中SESSION机制.分享给大家供大家参考.具体如下: 在Discuz! X中一如继往的,SESSION 并没有使用 PHP 自带的 SESSION 机制,而是系统的一套自带的机制. 在数据库中可以看到有两个 SESSION 表: 一个是pre_common_adminsession,是管理员登录后台的 SESSION 表: 另一个是 pre_common_session 表,是所有用户在前台浏览页面时的 SESSION 表. 这两个表都是内存表(内存表的读写速度远

大话Linux内核中锁机制之原子操作、自旋锁【转】

转自:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实是由于操作系统中存在多进程对共享资源的并发访问,从而引起了进程间的竞态.这其中包括了我们所熟知的SMP系统,多核间的相互竞争资源,单CPU之间的相互竞争,中断和进程间的相互抢占等诸多问题. 通常情况下,如图1所示,对于一段程序,我们的理想是总是美好的,希望它能够这样执行:进程1先对临界区完成操作,然

大话Linux内核中锁机制之RCU、大内核锁

大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linux内核中锁机制>系列博文进行了总结,并提出关于目前Linux内核中提供的锁机制的一些基本使用观点. 十.RCU机制 本节将讨论另一种重要锁机制:RCU锁机制.首先我们从概念上理解下什么叫RCU,其中读(Read):读者不需要获得任何锁就可访问RCU保护的临界

大话Linux内核中锁机制之原子操作、自旋锁

转至:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实是由于操作系统中存在多进程对共享资源的并发访问,从而引起了进程间的竞态.这其中包括了我们所熟知的SMP系统,多核间的相互竞争资源,单CPU之间的相互竞争,中断和进程间的相互抢占等诸多问题. 通常情况下,如图1所示,对于一段程序,我们的理想是总是美好的,希望它能够这样执行:进程1先对临界区完成操作,