RunTime知识整理

来源:互联网 发布:如何代理淘宝网店 编辑:程序博客网 时间:2024/06/07 06:32

RunTime

1. RunTime简介

因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时。也就是说只有编译器是不够的,还需要一个运行时系统 (runtime system) 来执行编译后的代码。这就是Objective-C Runtime 系统存在的意义,它是整个Objc运行框架的一块基石。

Runtime其实有两个版本:“modern”和 “legacy”。我们现在用的Objective-C 2.0 采用的是现行(Modern)版的Runtime系统,只能运行在 iOS 和 OS X 10.5 之后的64位程序中。而OS X较老的32位程序仍采用 Objective-C 1中的(早期)Legacy 版本的 Runtime 系统。这两个版本最大的区别在于当你更改一个类的实例变量的布局时,在早期版本中你需要重新编译它的子类,而现行版就不需要。

Runtime基本是用C和汇编(437版本开始较多使用mm文件,但是仍用C语法)实现的,可见苹果为了动态系统的高效而作出了很多努力,你可以在这里下到苹果维护的开源代码runtime源码。苹果和GNU各自维护一个开源的runtime版本,这两个版本之间都在努力的保持一致。

Objective-C基于RunTime具有相当多的动态特性,基本的也是经常被提到和用到的有动态类型(Dynamic typing),动态绑定(Dynamic binding)和动态加载(Dynamic loading)。

这些动态特性都是在Cocoa程序开发时非常常用的语言特性,而在这之后,OC在底层也提供了相当丰富的运行时的特性,比如枚举类属性方法、获取方法实现等等。

2. RunTime应用场景

a)               基本属性


b)              发送消息

调用方法底层是对象发消息

使用消息机制要#import <objc/message.h>

[receiver message];

// 底层运行时会被编译器转化为:

objc_msgSend(receiver, selector)

// 如果其还有参数比如:

[receiver message:(id)arg...];

// 底层运行时会被编译器转化为:

objc_msgSend(receiver, selector, arg1, arg2, ...)

 

 

-[__NSCFNumber lowercaseString]:unrecognized selector
sent to instance 0x87

 *** Terminatingapp due to uncaught exception
'NSInvalidArgumentException',reason:
'-[__NSCFNumberlowercaseString]:unrecognized selector sent to instance ox87'

上面这段异常信息是由NSObject的“doesNotRecognizeSelector:”方法所抛出的,此异常表明:消息接收者的类型是__NSCFNumber,而该接受者无法理解名位lowercaseString的选择子。

消息的转发分为两大阶段。第一阶段先征询接收者,所属的类,看其是否能动态添加方法,以处理当前这个“未知的选择子”(unknown selector),这叫做“动态方法解析”(dynamic method resolution)。第二阶段涉及“完整的消息转发机制”。如果运行期系统已经把第一阶段执行完了,那么接收者自己就无法再以动态新增方法的手段来响 应包含该选择子的消息了。此时,运行期系统会请求接受者以其他手段来处理与消息相关的方法调用。这又细分为两小步。首先,请接受者看看有没有其他对象处理 这条消息。若有,则运行期系统会把消息转给那个对象,于是消息转发过程结束,一起如常。若没有“备援的接收者”,则启动完整的消息转发机制,运行期系统会 把于消息有关的全部细节都封装到NSInvocation对象中,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。

 

1.    + (BOOL)resolveClassMethod:(SEL)sel;

2.    + (BOOL)resolveInstanceMethod:(SEL)sel;

//后两个方法需要转发到其他的类处理

3.    -(id)forwardingTargetForSelector:(SEL)aSelector;

4.    -(void)forwardInvocation:(NSInvocation*)anInvocation;

 

·                  第一个方法是当你调用一个不存在的类方法的时候,会调用这个方法,默认返回NO,你可以加上自己的处理然后返回YES。

·                  第二个方法和第一个方法相似,只不过处理的是实例方法。

·                  第三个方法是将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。

·                  第四个方法是将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。


其他常见方法:

#import<objc/runtime.h> :成员变量、类、方法

 

Ivar * class_copyIvarList:获得某个类内部的所有成员变量

 

Method *class_copyMethodList :获得某个类内部的所有方法

 

Method class_getInstanceMethod:获得某个实例方法(对象方法,减号-开头)

 

Method class_getClassMethod:获得某个类方法(加号+开头)

 

method_exchangeImplementations : 交换2个方法的具体实现

 

#import<objc/message.h> :消息机制

 

objc_msgSend(....)

 

-class方法返回对象的类;

 

-isKindOfClass:-isMemberOfClass:方法检查对象是否存在于指定的类的继承体系中(是否是其子类或者父类或者当前类的成员变量);

 

-respondsToSelector:检查对象能否响应指定的消息;

 

-conformsToProtocol:检查对象是否实现了指定协议类的方法;

 

-methodForSelector: 返回指定方法实现的地址。

 

c)               交换方法

#import "UIViewController+swizzling.h"
#import <objc/runtime.h>
 
@implementation UIViewController (swizzling)
 
//load方法会在类第一次加载的时候被调用
//调用的时间比较靠前,适合在这个方法里做方法交换
+ (void)load{
//方法交换应该被保证,在程序中只会执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
 
//获得viewController的生命周期方法的selector
SEL systemSel = @selector(viewWillAppear:);
//自己实现的将要被交换的方法的selector
SEL swizzSel = @selector(swiz_viewWillAppear:);
//两个方法的Method
Method systemMethod = class_getInstanceMethod([self class], systemSel);
        Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
 
//首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败
BOOLisAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (isAdd) {
//如果成功,说明类中不存在这个方法的实现
//将被交换方法的实现替换到这个并不存在的实现
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
}else{
//否则,交换两个方法的实现
         method_exchangeImplementations(systemMethod, swizzMethod);
        }
 
    });
}
 
- (void)swiz_viewWillAppear:(BOOL)animated{
    //这时候调用自己,看起来像是死循环
    //但是其实自己的实现已经被替换了
    [self swiz_viewWillAppear:animated];
    NSLog(@"swizzle");
}
@end

 

d)              动态添加方法

使用场景:一个类方法非常多,一次性加载到内存,比较耗费资源。为什么动态添加方法? OC都是懒加载,有些方法可能很久不会调用比如电商,视频,社交,收费项目:会员机制,要会员才拥有这些功能。


(方法解释详见“发送消息”)

e)               动态添加属性

在category中添加name属性但是并未与当前类进行关联,这样属性保存的地址和关联属性的保存地址是不同的。虽然同样可以完成显式的属性添加,但是当object对象销毁了,object.name并不会随着它的的销毁而销毁。这样显然不是关联关系。既然是添加属性,那么当object销毁了,object.name也应随之销毁。虽然直接在分类中实现setter和getter方法对新添加的属性进行赋值和输出的操作,但是并没有与之产生关联,所以这个时候需要使用runtime进行动态绑定,即将某个属性保存到调用它的对象里。你向哪个对象添加属性,那么就将这个属性保存到哪个对象中。


f)                字典转模型

使用场景:字典转模型时,希望可以不用与字典中属性一一对应


g)               解决是否由于多次连续点击造成重复请求

用运行时和分类,替换UIControl响应事件,根据响应的间隔时间来判断是否执行事件。

h)              NSCoding归档和解档


1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 游戏压缩出现未知错误或损坏怎么办 百度云里的压缩包解压后损坏怎么办 联创打印时显示压缩文件失败怎么办 电脑的软件打开出现未知格式怎么办 图片只突出人物边上全黑怎么办 合金机兵存档密码忘了怎么办 手机网页验证码无法加载插件怎么办 绝地求生次激战场机型不支持怎么办 木茷生存中文版安装包损坏了怎么办 手机下载软件显示安装包损坏怎么办 不小心把qq图片删了怎么办 奶水不足宝宝不好好吸奶怎么办 膀胱切除前列腺切除阴茎不硬怎么办 小孩的睾丸睾丸碰肿了怎么办 怎么判断小孩子的睾丸没下来怎么办 怀孕39周腰酸屁股酸疼该怎么办 我儿子18岁睾丸筋鼓起来怎么办 去医院检查说精子跑的慢怎么办 多囊卵巢综合症引起屁股增大怎么办 蚊子咬了肿了挠破了流水怎么办 血糖高引发的睾丸一直烂怎么办? 被洪水淹过的猪后期怎么办 做睾丸阴囊彩超阴茎突然勃起怎么办 阴茎冠状沟皮肤感染总不愈合怎么办 不小心咬到孩子破皮了怎么办 小孩子不小心碰到脸黑了一块怎么办 眼睛不小心碰到了里面红了怎么办 八个月宝宝睾丸还沒掉下来怎么办 孩子背部皮肤有一块皮肤很脏怎么办 洗浴种心搓背老板不给发工资怎么办 半个月小鸡屁骨下垂眼睛紧闭怎么办 在学校走廊把老师撞倒了怎么办 裤衩给孩子买的有点肥怎么办 到交警队立案后医疗费没了怎么办 交警扣车车里的贵重东西没了怎么办 睾丸内囊肿割了又长怎么办 我把别人的卵子踢碎了怎么办 两岁宝宝不小心被猫抓出血怎么办 两岁宝宝小蛋蛋肿了怎么办 吃大胺片过敏蛋皮又痛又痒怎么办 刚出生二十天宝宝蛋蛋有疝气怎么办