赞
踩
最近 研究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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。