oc 的 runtime机制(一)
来源:互联网 发布:公务员培训老师知乎 编辑:程序博客网 时间:2024/06/05 11:51
最近 研究oc中的runtime机制。先把一些研究体会记录下来。
首先:runtime 是什么?
runtime是一套比较底层的纯C语言和混编语言的静态库,对外提供了一些API, 属于1个C语言库, 包含了很多底层的C语言API,使得面向过程的C语言有了面向对象的能力。
在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者 。
为了验证,我们可以这样创建一个oc工程,通过命令:clang -rewrite-objc XXX(文件名)查看 该文件的底层实现。
<span style="font-size:14px;">#import <Foundation/Foundation.h>@interface Person : NSObject@property (nonatomic,strong)NSString *name;@property (nonatomic,assign)NSInteger age;@end</span>
main.h 中:(包含头文件:#include<objc/runtime.h> ,我们可以在这个文件里,学到runtime的具体使用,不过文件较多,我们应该只看关键的一些地方)
<span style="font-size:14px;">#import <Foundation/Foundation.h>#import "Person.h"#include <objc/runtime.h>int main(int argc, const char * argv[]) { @autoreleasepool { Person *person=[[Person alloc] init]; person.name=@"zhangsan"; person.age=25; } return 0;}</span>
然后到终端 进入mian.m 的目录下:输入命令 clang -rewrite-objc main.m ,则会将oc的main,变成底层的c++实现方式。
对比 oc 中 的main.h
<span style="font-size:14px;">int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; Person *person=((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init")); ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)person, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_p8_j5zl7s615xv5d0cpq31nsn680000gn_T_main_d0e88f_mi_0); ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)person, sel_registerName("setAge:"), (NSInteger)25); } return 0;}</span>
可见到 :
OC中的Person *person=[[Pserson alloc] init];
在底层实现是这样的:
objc_msgSend (
objc_msgSend(objc_msgSend(objc_getClass(“Pserson”),sel_registerName("alloc")
),sel_registerName("init”) );
上述 还可以看到几个方法:
objc_msgSend 可以给对象发送消息
objc_getClass(“Person”) 可以获取到指定名称的对象
sel_registerName(“alloc”) 可以调用到对象的方法
为了更细致的了解 oc的runtime机制,我们可以进入 runtime.h 文件中学习。( #include<objc/runtime.h> )
首先:看Class:
typedefstructobjc_class *Class;
struct objc_class {
Class isa; // 指向metaclass
Class super_class ; // 指向其父类
const char *name ; // 类名
long version ; // 类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改、读取
long info; // 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
long instance_size ; // 该类的实例变量大小(包括从父类继承下来的实例变量);
struct objc_ivar_list *ivars; // 用于存储每个成员变量的地址
struct objc_method_list **methodLists ; // 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法;
struct objc_cache *cache; // 指向最近使用的方法的指针,用于提升效率;
struct objc_protocol_list *protocols; // 存储该类遵守的协议
}
/* Use `Class` instead of `struct objc_class *` */
再来看 Object:
struct objc_object
{
Class isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;
由此可见,
1:Class是一个指向objc_class结构体的指针,而每个Class里还有一个成员isa,isa也是一个指向objc_class结构体的指针。id是一个指向objc_object的结构体指针。
2:类 比 对象 的结构体中 多了众多的成员。关于 isa:
isa:objec_object(对象)中isa指针指向的类结构称为class(也就是该对象所属的类),其中存放着普通成员变量与对象方法 (“-”开头的方法);然而class中的isa指向的类结构称为metaclass,其中存放着static类型的成员变量与static类型的方法 (“+”开头的方法)。
oc中的方法调用 是这样的:
1、当我们调用某个对象的对象方法时,它会首先在自身isa指针指向的类(class)的cache中 通过SEL查找对应函数method(猜测cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。
2、当我们调用某个类方法时,它会首先通过自己的isa指针找到metaclass,在Class中先去cache中 通过SEL查找对应函数method(猜测cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。
其实,oc中runtime的核心就是 消息机制。这个 后面会在分享。
那么 什么时候我们 直接使用runtime的机制?
1> 当需要非常高的性能开发时,使用runtime,注释:oc的代码已经无法满足性能需求
2> 当我们对系统内部的实现很好奇的时候,可以用clang反编译成c++去看底层的实现机制
相关技术文档:http://www.cocoachina.com/cms/plus/search.php
官方文档阐述runtime:http://blog.csdn.net/deep_explore/article/details/7477637
练习使用 runtime提供的 API
给一个类添加 属性:
#import "BtnView.h"#import <objc/runtime.h>@interface BtnView()@property (nonatomic,strong)UILabel *label;@end@implementation BtnView// 唯一 地址static char labelAddress;-(UILabel *)label{ return objc_getAssociatedObject(self, &labelAddress);}-(void)setLabel:(UILabel *)label{ objc_setAssociatedObject(self, &labelAddress, label, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}-(instancetype)initWithFrame:(CGRect)frame{ if(self=[super initWithFrame:frame]){ self.label=[[UILabel alloc] initWithFrame:self.bounds]; self.label.text=@"run time 核心:消息分发"; self.label.textAlignment=NSTextAlignmentCenter; self.label.textColor=[UIColor blueColor]; [self addSubview:self.label]; } return self;}-(void)test{ NSLog(@"_objc_sendMessage not found Object");}@end
消息分发:
#import "ViewController.h"#import <objc/runtime.h>#import "BtnView.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // // 1: OC 调用函数方法// [self createView]; //2: runtime 语法 //向类中添加一个方法。 objc_msgSend(self,@selector(createView)); NSMutableArray *ary=[NSMutableArray array]; objc_msgSend(ary, @selector(addObject:),@"1"); NSLog(@"%@",ary); }- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}#pragma mark -- 创建视图-(void)createView{// BtnView *btnView=[[BtnView alloc] initWithFrame:CGRectMake(10, 50, self.view.bounds.size.width-20, 40)]; BtnView *btnView=(BtnView *) objc_msgSend(objc_msgSend(objc_getClass("BtnView"), sel_registerName("alloc")), sel_registerName("initWithFrame:"),CGRectMake(10, 50, self.view.bounds.size.width-20, 40)); [self.view addSubview:btnView];}@end
- oc 的 runtime机制(一)
- OC的Runtime(运行时)机制的解析
- OC的Runtime(运行时)机制的解析
- OC runtime机制
- RunTime机制详解(一)
- runtime的那点事儿(一)消息机制
- Runtime的那点事儿(一)消息机制
- OC runtime学习笔记一
- oc 的runtime
- oc的Runtime
- OC的runtime运行机制
- 理解OC的Runtime
- iOS RunTime 机制浅析(一)
- Android runtime机制(一)init进程
- OC Runtime(一)一些底层实现原理
- OC基础:runtime机制与isa指针
- iOS开发 -OC之 runtime机制
- OC反射机制一
- vs2010断点调试技巧分享
- 作业4.19
- JavaScript——面向对象初理解
- Oracle SQL性能优化
- LXC之namespace模块学习-小结
- oc 的 runtime机制(一)
- C语言常用面试题_2015.5.6(2)
- jconcole
- 杭电 hdoj 1003 动态规划 c语言题解
- iOS uitextFiled输入框被软键盘遮盖怎么办
- 第一个JAVA程序-HelloWorld
- SpringMVC入门示例教程(一)
- 漫谈数据库索引
- 关于继承parent="Theme.AppCompat"错误:This Activity already has an action bar supplied by the window decor