oc 的 runtime机制(一)

最近 研究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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值