runtime运行机制方法学习

runtime这玩意第一次听说时都不知道是什么,经过了解后才知道它就是oc动态语言的机制,没有它那oc就不能称为动态语言。在之前可能大家对runtime了解都不深,随着编程技能的日益加深和需要,大家开始更加关心底层的实现,并用自己更需要的方式实现。这时runtime开始慢慢火起来了,作为一个iOS程序员,如果出去说自己不知道runtime无疑是一件很丢分的事情。由于runtime的底层实现笔者也没搞得太明白,在这里就跟大家提提几个关于runtime的方法。

1.runtime之动态添加属性

在最开始学习oc之时,我相信大家都听过,在类目中是无法添加属性的。而我在这里将要实现给UIButton增加一个block属性,方便实现其点击方法。首先创建一个UIButton的类目(UIButton+Block),接着我们来实现

1 UIButton+Block.h文件
2 //定义一个block
3 typedef void(^ActionBlock)(UIButton *sender);
4
5 @interface UIButton (Block)
6 //定义方法当button通过某点击状态使用一个block
7 - (void)handleClickEvent:(UIControlEvents)aEvent UsingBlock:(ActionBlock)block;
8 @end

.h文件较为简单一看明白不多做解释,接下来是.m文件

 1 //定义添加属性所对应的关键字
 2 static char *overViewKey;
 3 @implementation UIButton (Block)
 4
 5 - (void)handleClickEvent:(UIControlEvents)aEvent UsingBlock:(ActionBlock)block{
 6     //设置关联对象 (被关联者,关键字,关联者,属性状态)
 7     objc_setAssociatedObject(self, &overViewKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
 8     //给button增加点击事件
 9     [self addTarget:self action:@selector(buttonClicked:) forControlEvents:aEvent];
10 }
11
12 - (void)buttonClicked:(UIButton *)sender{
13     //获取关联对象 (被关联者,关键字)
14     ActionBlock clickedBlock = objc_getAssociatedObject(self, &overViewKey);
15     //如果block存在则调用block
16     if (clickedBlock != nil) {
17         clickedBlock(sender);
18     }
19 }

在这里 objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)

便为runtime方法。此方法有4个参数,第一个为类别:self(给谁增加属性),第二个参数为属性关键字:&overViewKe(作为属性的标示符来查找属性),第三个参数为属性:block(要添加的属性),第四个参数为属性修饰符:OBJC_ASSOCIATION_COPY_NONATOMIC(作为属性的修饰符,nonatomic,copy)。通过此方法我们便可以动态的给一个类增加属性了。

属性增加后我们便需要获取此属性 objc_getAssociatedObject(<#id object#>, <#const void *key#>),此方法就较为简单了。在这里需要注意的是属性关键字,由于要保证属性关键字的作用域,所以在这里使用static作为其修饰符,另外由于runtime机制属于c语言实现,因此这里的关键字需要使用char类型!

这里定义好后的使用就简单了直接一句代码使用

1 //定义一个uibutton 并使用其方法
2 [button handleClickEvent:UIControlEventTouchUpInside UsingBlock:^(UIButton *sender) {
3         NSLog(@"你点了%@按钮",[sender currentTitle]);
4     }];

这样通过runtime改写的是UIButton的点击事件就更方便理解,阅读了。

2.runtime之归档使用方法

在自定类归档时,我们需要将属性一个一个的写入,在属性较少时可能不觉得有什么问题,但若是属性较多时,此种写法就显得笨拙了,此处我们便利用runtime的方法来动态的获取其属性,并写入。

首先自己新建一个类,这里笔者创建一个BQPerson类

 1 BQPerson.h文件
 2 @interface BQPerson : NSObject <NSCoding>
 3 //假定这里有好几十个属性
 4 @property (nonatomic, copy) NSString *name;
 5 @property (nonatomic, copy) NSString *crad;
 6 @property (nonatomic, copy) NSString *birthday;
 7 @property (nonatomic, assign) NSInteger age;
 8 @property (nonatomic, copy) NSString *iphone;
 9 @property (nonatomic, copy) NSString *adress;
10 @property (nonatomic, assign) CGFloat height;
11 @property (nonatomic, assign) CGFloat weight;
12
13 @end

接下来我们便在.m文件中通过runtime来实现其归档

 1 - (id)initWithCoder:(NSCoder *)aDecoder{
 2     if (self = [super init]) {
 3         //定义长度
 4         unsigned int index = 0;
 5         //动态获取成员变量数组
 6         Ivar *ivars = class_copyIvarList([self class], &index);
 7         for (int i = 0 ; i < index; i++) {
 8             //取出变量
 9             Ivar ivar = ivars[i];
10             //获取变量名
11             const char *name = ivar_getName(ivar);
12             //输出属性名字及其在内存偏移量
13             NSLog(@"%s %td",name,ivar_getOffset(ivar));
14             //解档:解档调用方法decodeObjectForKey
15             NSString *key = [NSString stringWithUTF8String:name];
16             id value = [aDecoder decodeObjectForKey:key];
17             //赋值
18             [self setValue:value forKey:key];
19         }
20         //由于这里是使用的C语言,需要自己手动管理内存
21         free(ivars);
22     }
23     return self;
24 }
25
26 - (void)encodeWithCoder:(NSCoder *)aCoder{
27     //定义长度
28     unsigned int index = 0;
29     //动态获取类成员变量数组
30     Ivar *ivars = class_copyIvarList([self class], &index);
31     for (int i = 0; i < index; i++) {
32         //取出对应的变量
33         Ivar ivar = ivars[i];
34         //获取取出的变量名
35         const char *name = ivar_getName(ivar);
36         //归档:归档调用方法encodeObject: forKey:
37         NSString *key = [NSString stringWithUTF8String:name];
38         id value = [self valueForKey:key];
39         [aCoder encodeObject:value forKey:key];
40     }
41     //释放获取的变量数组
42     free(ivars);
43 }

在这里我们首先使用 class_copyIvarList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>) 方法来获取一个类的成员变量数组,其中第一个参数为一个类:[self class](获取哪个类的成员变量),第二个参数为一个无符号整型指针:index(用以记录数组长度)。接下来我们通过ivar_getName(<#Ivar v#>)来返回一个变量的名字,接着将此名字转化为NSString类型。便可以进行解档和归档了。需要注意的是这里由于使用的是C语言,所以数组需要自己手动释放!

3.runtime之方法调换

在工程项目变得较为庞大后,由于功能需要我们要实现2个方法的交换,若是在代码里一个一个修改相对来说较为困难,在这里我们便可以使用runtime通过几句代码来直接交换我们需要交换的2个方法。示例如下:

 1 + (void)load{
 2     static dispatch_once_t onceToken;
 3     dispatch_once(&onceToken, ^{
 4         Method testfirst = class_getInstanceMethod(self, @selector(testfirst));
 5         Method secondtest = class_getInstanceMethod(self, @selector(secondtest));
 6         if (testfirst != nil && secondtest != nil) {
 7             method_exchangeImplementations(testfirst, secondtest);
 8         }else{
 9             NSLog(@"方法获取失败");
10         }
11     });
12 }
13
14 - (void)viewDidLoad {
15     [super viewDidLoad];
16
17     [self testfirst];
18     [self secondtest];
19 }
20
21 - (void)testfirst{
22     NSLog(@"1===%s",__func__);
23 }
24 - (void)secondtest{
25     NSLog(@"2===%s",__func__);
26 }

按照正常程序来讲在viewDidLoad中应该是先走testfirst方法,接着再走secondtest方法,但这里我们在load(类第一次加载进内存的时候调用)方法中使用

class_getInstanceMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>)返回一个方法,其中第一个参数:self(类实例对象),第二个参数:@selector(testfirst)(方法选择器,选择一个方法)。最后我们使用method_exchangeImplementations(<#Method m1#>, <#Method m2#>) 将获取的2个方法进行动态的交换,这样在交换后的方法实现中,此2种方法都会互换,需要注意的是此处交换后,交换的是2个方法的实现地址,如调用[self testfirst]方法时会走secondtest的方法!

4.runtime之使assgin修饰符具有weak修饰符的特性(对象不存在后置为nil特性)

由于此方法实用性不强,只作为理论补充所以简单描述即可,首先给NSObject对象增加一个属性,若对象被销毁前都销毁其属性,那么在属性被销毁的时候就将对象置为nil。具体其做法如下

4.1.重写assgin的set方法

- (void)setTestView:(UIView *)testView{
    _testView = testView;
    [testView psp_rundealloc:^{
        _testView = nil;
    }];
}

4.2.写一个NSObject类目,动态增加一个属性

- (void)psp_rundealloc:(void (^)())block{
    if (block) {
        PSPObjectBlock *pspobjc = [[PSPObjectBlock alloc] initWithBlock:block];
        objc_setAssociatedObject(self, "pspobjc", pspobjc, OBJC_ASSOCIATION_RETAIN);
    }
}

4.3.pspobjc属性内部实现如下

@interface PSPObjectBlock(){
    BlockTest _block;
}
@end
@implementation PSPObjectBlock
- (instancetype)initWithBlock:(BlockTest)block{
    self = [super init];
    if (self) {
        _block = [block copy];
    }
    return self;
}
//销毁时调用block使非weak属性值变为nil
- (void)dealloc{
    if (_block) {
        _block();
    }
}

有兴趣的朋友可以试一试实现此方法,关于runtime知识的记录分享就到这里了,如果其中有什么错误,望大家指出,谢谢!

时间: 2024-08-29 16:15:16

runtime运行机制方法学习的相关文章

runtime运行机制

这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! 第一个问题, 1>runtime实现的机制是什么,怎么用,一般用于干嘛? runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者 比如说,下面一个创建对象的方法中, 对比举例: OC : [[MJPerson alloc] init] runtime

OC的runtime运行机制

什么是runtime runtime就是一套底层的c语言API(Application Programming Interface)里面包括很多强大实用的c语言类型.c语言函数. 实际上,平时我们编写的oc代码,底层都是基于runtime实现的. 也就是说我们平时编写的oc代码最终都是转成了底层的runtime代码(c语言代码) . [obj say] —> 调用id objc_msgSend(id self, SEL op, ...)函数 -> 里面的obj.isa指针-> class

oc - runtime运行机制

Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时做的事放到了运行时来处理.同时OC也是一门简单的语言,很大一部分是C的内容,只是在语言层面上加了关键字和语法,真正让OC强大的是它的运行时,它很小却很强大,其中核心是消息分发.这种动态语言的优势在于:我们写代码时更加灵活,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现. 这种特性意味着OC不仅需要一个编译器,还需要一个运行时系统来执行编译的代码.对于OC来说,这个运行时系统就像一个操作系统一样.这个运行时系

从一个面试题牵出的关于JavaScript运行机制的学习

今天在偶然在网上看到一个JavaScript的面试题,尝试着看了一下,很正常的就做错了,然后给我们前端做,哈哈,他居然也顺理成章做的错了,代码大概是这样的 /*1 下面代码会怎样执行?执行结果是什么*/ var i=true; setTimeout('stoploops()', 2000); loops(); function loops(){ alert('no hello world!'); while(i){ } } function stoploops(){ i=false; alert

Runtime运行时机制原理、用途

首先我们先了解一下runtime实现机制及作用 1.runtime实现的机制是什么? runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者比如说,下面一个创建对象的方法中, 举例: OC : [[MJPerson alloc] init] runtime : objc_msgSend(objc_msgSend(“MJPe

runtime 运行时机制 完全解读(二)

原文地址:http://my.oschina.net/panyong/blog/298631 我们前面已经讲过一篇runtime 原理,现在这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! 首先,第一个问题, 1>runtime实现的机制是什么,怎么用,一般用于干嘛? 这个问题我就不跟大家绕弯子了,直接告诉大家, runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是

runtime 运行时机制 完全解读

runtime 运行时机制 完全解读 目录[-] import import 我们前面已经讲过一篇runtime 原理,现在这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! 首先,第一个问题, 1>runtime实现的机制是什么,怎么用,一般用于干嘛? 这个问题我就不跟大家绕弯子了,直接告诉大家, runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runti

runtime 运行时机制[转]

转自:http://my.oschina.net/panyong/blog/297640 对于runtime机制,在网上找到的资料大概就是怎么去用这些东西,以及查看runtime.h头文件中的实现,当然这确实是一种很好的学习方法,但是,其实我们还是不会知道runtime底层编译成C++语言之后做了什么? 查到一个大牛给资料,顿时对runtime有了一定认识! 我们随便写一个小程序,代码如下: person类头文件如下, 1 #import <Foundation/Foundation.h> 2

?runtime 运行时机制

?runtime 运行时机制 完全解读 摘要 在最开始听到runtime的时候,我是感到恐惧的,多么高大上的东西啊!!!后来,开始在网上查一些资料,可是就是只有那么几篇,看了好久,还不知所云,所以就更加恐惧了!!!!后来经过查看documents 以及一些国外大牛的blogs,终于对runtime有了更深刻的了解!于是就想写下这些东西,希望对读者们有帮助…… runtime 运行时机制 详细解读 目录[-] import import 我们前面已经讲过一篇runtime 原理,现在这篇文章主要介