1.0 Runtime介绍
- 1.1 Runtime简介
因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时。
也就是说只有编译器是不够的,还需要一个运行时系统 (runtime system) 来执行编译后的代码。
这就是 Objective-C Runtime 系统存在的意义,它是整个Objc运行框架的一块基石。
Runtime基本是用C和汇编写的,可见苹果为了动态系统的高效而作出的努力。
Runtime其实有两个版本:“modern”和 “legacy”。
1.我们现在用的 Objective-C 2.0 采用的是现行(Modern)版的Runtime系统,只能运行在 iOS 和 OS X 10.5 之后的64位程序中。
2.而OS X较老的32位程序仍采用 Objective-C 1中的(早期)Legacy 版本的 Runtime 系统。
3.这两个版本最大的区别在于当你更改一个类的实例变量的布局时,在早期版本中你需要重新编译它的子类,而现行版就不需要。
4.苹果和GNU各自维护一个开源的runtime版本,这两个版本之间都在努力的保持一致。
- 1.2 Runtime总结
1.OC就是运行时机制
2.Runtime是消息机制
3.OC方法调用的本质就是发送消息
- 1.3 Objective-C 从三种不同的层级上与 Runtime 系统进行交互
1.分别是通过 Objective-C 源代码
2.通过 Foundation 框架的NSObject类定义的方法
3.通过对 runtime 函数的直接调用。
2.0 Runtime使用
- 2.1 Runtime作用
总结起来无非是:
可以在运行时,在不继承也不category的情况下,为各种类(包括系统的类)做很多操作,具体包括:
增加
1.增加函数:class_addMethod
2.增加实例变量:class_addIvar
3.增加属性:@dynamic标签,或者class_addMethod,因为属性其实就是由getter和setter函数组成
4.增加Protocol:class_addProtocol (说实话我真不知道动态增加一个protocol有什么用,-_-!!)
获取
1.获取函数列表及每个函数的信息(函数指针、函数名等等):class_getClassMethod method_getName ...
2.获取属性列表及每个属性的信息:class_copyPropertyList property_getName
3.获取类本身的信息,如类名等:class_getName class_getInstanceSize
4.获取变量列表及变量信息:class_copyIvarList
5.获取变量的值
替换:
1.将实例替换成另一个类:object_setClass
2.将函数替换成一个函数实现:class_replaceMethod
3.直接通过char *格式的名称来修改变量的值,而不是通过变量
- 2.2 Runtime使用细节
1.使用Runtime则必须导入
#import <objc/message.h> 或者 #import<objc/runtime.h>
// 前者包含后者
2. Runtime函数的命名规则: 谁的事情,谁开头
例如: objc_getClass:获取类对象
objc_getMetaClass:获取元类
3.0 用到的OC常识
- 3.1 OC常识
1. OC对象方法都保存在类对象中方法列表中,OC对象中类方法保存在元类(meta)
2. OC中所有方法最终都会转换成函数
3. 内存五大区:栈,堆,常量区,全局区,方法区
a. 栈:自动管理内存
b. 堆:手动管理内存
- 3.2 OC方法调用流程:
1.根据对象的isa去对应类对象去查找方法, isa:指向类对象
2.根据传入方法编号,去类对象中方法列表中查找对应方法
3.调用方法实现
4.0 Runtime常用方法示例
- 4.1 Runtime(消息机制)
- 首先,你需要知道这两个概念:
1.OC中调用方法就是向对象发送消息。
比如 :[person run];
这实际上这是在给person这个对象发送run这个消息。
2.当run这个方法只有定义没有实现就会报错:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person run]: unrecognized selector sent to instance
经典消息发送
消息转发
-
4.2 获取类的所有方法列表
获取到的数据是一个Method数组,Method数据结构中包含了函数的名称、参数、返回值等信息,以下代码以获取名称为例:
u_int count;
Method * methods= class_copyMethodList([UIView class], &count);
for (int i = 0; i < count ; i++)
{
SEL name = method_getName(methods[i]);
NSString *strName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
NSLog(@"%@",strName);
}
- 4.3 Runtime(交换方法)
- 交换方法实现 : 调用A的方法名但是会去执行B的方法.
- ###### !!!友情提醒 : 不要为了使用runtime,而去使用(超级容易坑队友)
- 使用的一些场景 :
-
1.给系统的方法增加功能
-
例如 : 给UIImage的imageNamed方法提供一个功能,加载图片的时候,会告诉开发者是否加载功能
//TODO: 在原来加载图片功能之上,在添加一个判断是否加载成功功能
1.自定义类,重写系统方法 弊端:1.每次使用,都需要导入
2.给UIImage提供分类,扩充方法 弊端:1.每次使用,都需要导入
tips: 2.1 在分类中,重写系统方法实现,会导致系统方法被干掉
2.2 在分类中,重写系统方法实现,容易坑到队友,慎用
3.交换方法 交换imageNamed和bl_imageNamed实现
3.1 根据OC方法调用的流程,我们只需要交换方法列表映射就可以实现方法交换
![交换方法实现原理](http://upload-images.jianshu.io/upload_images/1832614-588edb92e310123c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Runtime代码实现方法交换 :
ViewController.m 文件
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// bl_imageNamed有加载图片和判断功能
[UIImage imageNamed:@"123"];
}
UIImage+Image.m 文件
import "UIImage+Image.h"
import <objc/message.h>
@implementation UIImage (Image)
/*load方法特性:
1.程序一开始启动,就会把所有类加载进内存,此时就会调用load方法
2.只会调用一次
*/
-
(void)load
{
// 所有方法都保存在类中
// Class:获取哪个类方法 [xxxx class]
// SEL:获取哪个方法Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
Method bl_imageNameMethod = class_getClassMethod(self, @selector(bl_imageNamed:));// 交换imageNamed和bl_imageNamed实现
method_exchangeImplementations(imageNameMethod, bl_imageNameMethod);
} -
(UIImage *)bl_imageNamed:(NSString *)name
{
// 1.加载图片
// 2.由于交换了映射 :在bl_imageNamed方法内部调用bl_imageNamed,实际是调用了imageNamed
UIImage *image = [UIImage bl_imageNamed:name];// 3.判断是否加载成功
if (image == nil) {
NSLog(@"加载失败");
} else {
NSLog(@"加载成功");
}
return image;
}
- TODO : Runtime(动态添加方法)
留坑
- TODO :Runtime(动态添加属性)
留坑
- ### Runtime 一些常见的问题与解决方法
Q1 : 怎么才能让消息机制函数有参数提示?
A1 : runtime的消息机制函数,在xcode6之后就没有提示参数
S1 : 点击工程文件 -> build Setting -> 搜索msg -> Enable Strict Checking of objc_msgSend Calls(不要严肃检查消息机制调用) -> NO
OC_runtime运行时官方文档翻译:http://www.ithao123.cn/content-801906.html