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++去看底层的实现机制




关于runtime的详细学习地址:

相关技术文档: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














0 0