码迷,mamicode.com
首页 > 移动开发 > 详细

iOS之RunTime浅谈

时间:2016-04-08 06:36:19      阅读:300      评论:0      收藏:0      [点我收藏+]

标签:

首先说一下什么是runtime:
RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用 在编译的时候会决定调用哪个函数( C语言的函数调用请看这里 )。编译完成之后直接顺序执行,无任何二义性。OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。OC的代码要创造并跑起来必不可少的是XCode的IDE和运行时框架。
举例说明:
比如你[obj makeText];
则运行时就这样的:首先,编译器将代码[obj makeText];转化为objc_msgSend(obj, @selector (makeText));,在objc_msgSend函数中。首先通过obj的isa指针找到obj对应的class。在Class中先去cache中 通过SEL查找对应函数method(猜测cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。

Runtime就是运行时,一个程序开发的过程通常可以分为以下阶段,编辑-预编译-编译-连接-运行,运行时可以说就是我们的程序再运行的阶段发生的一些事情,在这个阶段程序通常会把一些OC的代码转化成C语言的代码,从而提高执行的效率,在这个阶段我们也可以动态的为某个对象的属性赋值,而对象的属性具体是什么类型也会在这个阶段进行确定(NSString *str = [NSData data]; 其中str在编译的时候是NSString类型,运行的时候是NSData类型)。系统也提供了Runtime的类库,让我们可以直接调用一些运行时把OC代码转化C之后的代码比如:objc_msgSend();同样也可以通过运行时,为分类添加属性,需要用到objc_getAssociatedObject和objc_setAssociatedObject函数

总结起来,iOS中的RunTime的作用有以下几点:

1.发送消息(obj_msgSend)

2.方法交换(method_exchangeImplementations)

3.消息转发

4.动态添加方法

5.给分类添加属性

6.获取到类的成员变量及其方法

7.动态添加类

下面我想通过一个小demo说一下方法交换:

首先我觉得既然运行时是可以实现方法的交换我觉得类似于切面编程中的切面处理逻辑(即在某一个执行过程时刻执行你的逻辑),这种情况可能需要在你扩充一些系统现有的方法的时候用到(把系统的方法扩展为自己的方法,自己的方法可以既有系统的方法还可以自己扩充一些自定义的东西)。另外一个是假如你在一个比较多逻辑的app中大量用到了同一个方法的逻辑块,突然你想要换掉这块逻辑,而依次换是不现实的,这时候你只需要知道方法的名字就可以利用运行时执行替换的方法来把原来的方法逻辑换成新的。

下面看代码:

首先建立一个项目(我们都知道页面在退出的时候或者生命周期哟啊结束的时候是会调用它的dealloc方法的,我们为了说明方法的替换就把系统的dealloc方法替换成我们自己的方法):

技术分享

我觉得要实现方法的交换就要找好交换点,也就是那个切面的东西,这里好像一般的runtime都会选择写在你要交换方法的类的类目里,这里新建一个UIViewController的类目文件为UIViewController+MyExtension,.m文件里的实现如下:

#import "UIViewController+MyExtension.h"
#import <objc/runtime.h>
@implementation UIViewController (MyExtension)
+ (void)load{
    Method method1 = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));
    Method method2 = class_getInstanceMethod(self, @selector(my_dealloc));
    method_exchangeImplementations(method1, method2);
}
- (void)my_dealloc{
    NSLog(@"%@页面已经退出,这是我的实现...",self);
}

因为所有的页面都会走load方法 所以选择切面点为重写的load方法中,class_getInstanceMethod为获得实例方法(就是减号方法),如果要获得类方法需要用class_getClassMethod,此时我们在UIViewController的类目里写方法,那么每个UIViewController都会走这个类目里重写的load方法,我们在页面销毁的时候就会走my_dealloc方法,先看一下程序的运行效果:

技术分享

这个是MainController页面,点击下一页会推出FirstViewController:

技术分享

此时点击back回退,那个这个时候FirstViewController页面就会被销毁,系统此时会自动钓鱼dealloc方法,而我们此时替换了dealloc方法,那么我们就会在控制台得到:

技术分享

此时我们就实现了交换方法的目的,当然此时你在调用我自定义的方法的时候就恰好是执行的系统的方法,因为发生了交换。

下面再通过一个小demo说一下给分类添加属性

首先还是建立一个项目:

技术分享

本示例目的在于通过调用button类目里的一个block实现button的点击事件,需要建立一个类目文件:
UIButton+aa.h文件:

#import <UIKit/UIKit.h>
//定义一个block 参数为NSIndexPath类型 返回值为void
typedef void(^ButtonAction) (NSIndexPath *index);
@interface UIButton (aa)

@property (nonatomic,strong) NSIndexPath *index;

//ButtonAction类型的block变量buttonAction
@property (nonatomic,copy) ButtonAction buttonAction;

@end

UIButton+aa.m文件:

#import "UIButton+aa.h"
#import <objc/runtime.h>

@interface UIButton()
@end

const void *key = "key";
const void *key1 = "key1";

@implementation UIButton (aa)
//set get方法自己写
@dynamic index;
@dynamic buttonAction;

//Index属性的set方法
- (void)setIndex:(NSIndexPath *)index{
    
    //给谁绑定 key值  绑定的值 //语义修饰
    objc_setAssociatedObject(self, key, index, OBJC_ASSOCIATION_RETAIN);
}

//Index属性的get方法
- (NSIndexPath *)index{
    return objc_getAssociatedObject(self, key);
}

//ButtonAction这个block属性的set方法
- (void)setButtonAction:(ButtonAction)buttonAction{
    
    //这里在UIButton的类目里在block的set方法里给当前的self(UIButton对象)添加一个addTarget,当UIButton类型的对象调用这个ButtonAction的block时候相当于就是实现了一个点击事件
    [self addTarget:self action:@selector(selfAction) forControlEvents:UIControlEventTouchUpInside];
    
    //给谁绑定 key值  绑定的值  语义修饰
    objc_setAssociatedObject(self, key1, buttonAction, OBJC_ASSOCIATION_COPY);
}

//ButtonAction这个block属性的get方法
- (ButtonAction)buttonAction{
    return objc_getAssociatedObject(self, key1);
}

//实现点击事件
- (void)selfAction{
    if (self.buttonAction) {//判断block有没有被初始化
        self.buttonAction([NSIndexPath indexPathForRow:1 inSection:3]);
    }
}

@end


 

在ViewController里调用的时候:

- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *but = [UIButton buttonWithType:(UIButtonTypeSystem)];
    but.index = [NSIndexPath indexPathForRow:0 inSection:1];
    but.buttonAction = ^(NSIndexPath *index){
        NSLog(@"%@",index);
    };

    but.frame = CGRectMake(100, 100, 100, 100);
    but.backgroundColor = [UIColor redColor];
    [self.view addSubview:but];
}

此时页面运行效果为:

技术分享

 

iOS之RunTime浅谈

标签:

原文地址:http://www.cnblogs.com/yk123/p/5366331.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!