当前位置:   article > 正文

oc 的 runtime机制(一)_oc runtime机制

oc runtime机制

最近 研究oc中的runtime机制。先把一些研究体会记录下来。


首先:runtime 是什么?

  

runtime是一套比较底层的纯C语言和混编语言的静态库,对外提供了一些API, 属于1个C语言库, 包含了很多底层的C语言API,使得面向过程的C语言有了面向对象的能力。 

在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者 。

为了验证,我们可以这样创建一个oc工程,通过命令:clang -rewrite-objc XXX(文件名)查看 该文件的底层实现。


  1. <span style="font-size:14px;">#import <Foundation/Foundation.h>
  2. @interface Person : NSObject
  3. @property (nonatomic,strong)NSString *name;
  4. @property (nonatomic,assign)NSInteger age;
  5. @end</span>


main.h 中:(包含头文件:#include<objc/runtime.h> ,我们可以在这个文件里,学到runtime的具体使用,不过文件较多,我们应该只看关键的一些地方)

  1. <span style="font-size:14px;">#import <Foundation/Foundation.h>
  2. #import "Person.h"
  3. #include <objc/runtime.h>
  4. int main(int argc, const char * argv[]) {
  5. @autoreleasepool {
  6. Person *person=[[Person alloc] init];
  7. person.name=@"zhangsan";
  8. person.age=25;
  9. }
  10. return 0;
  11. }</span>


然后到终端 进入mian.m 的目录下:输入命令  clang -rewrite-objc main.m ,则会将oc的main,变成底层的c++实现方式。

对比 oc 中 的main.h

  1. <span style="font-size:14px;">int main(int argc, const char * argv[]) {
  2. /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
  3. 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"));
  4. ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)person, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_p8_j5zl7s615xv5d0cpq31nsn680000gn_T_main_d0e88f_mi_0);
  5. ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)person, sel_registerName("setAge:"), (NSInteger)25);
  6. }
  7. return 0;
  8. }</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


给一个类添加 属性:

  1. #import "BtnView.h"
  2. #import <objc/runtime.h>
  3. @interface BtnView()
  4. @property (nonatomic,strong)UILabel *label;
  5. @end
  6. @implementation BtnView
  7. // 唯一 地址
  8. static char labelAddress;
  9. -(UILabel *)label
  10. {
  11. return objc_getAssociatedObject(self, &labelAddress);
  12. }
  13. -(void)setLabel:(UILabel *)label
  14. {
  15. objc_setAssociatedObject(self, &labelAddress, label, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  16. }
  17. -(instancetype)initWithFrame:(CGRect)frame
  18. {
  19. if(self=[super initWithFrame:frame]){
  20. self.label=[[UILabel alloc] initWithFrame:self.bounds];
  21. self.label.text=@"run time 核心:消息分发";
  22. self.label.textAlignment=NSTextAlignmentCenter;
  23. self.label.textColor=[UIColor blueColor];
  24. [self addSubview:self.label];
  25. }
  26. return self;
  27. }
  28. -(void)test
  29. {
  30. NSLog(@"_objc_sendMessage not found Object");
  31. }
  32. @end

消息分发:


  1. #import "ViewController.h"
  2. #import <objc/runtime.h>
  3. #import "BtnView.h"
  4. @interface ViewController ()
  5. @end
  6. @implementation ViewController
  7. - (void)viewDidLoad {
  8. [super viewDidLoad];
  9. // Do any additional setup after loading the view, typically from a nib.
  10. // // 1: OC 调用函数方法
  11. // [self createView];
  12. //2: runtime 语法
  13. //向类中添加一个方法。
  14. objc_msgSend(self,@selector(createView));
  15. NSMutableArray *ary=[NSMutableArray array];
  16. objc_msgSend(ary, @selector(addObject:),@"1");
  17. NSLog(@"%@",ary);
  18. }
  19. - (void)didReceiveMemoryWarning {
  20. [super didReceiveMemoryWarning];
  21. // Dispose of any resources that can be recreated.
  22. }
  23. #pragma mark -- 创建视图
  24. -(void)createView
  25. {
  26. // BtnView *btnView=[[BtnView alloc] initWithFrame:CGRectMake(10, 50, self.view.bounds.size.width-20, 40)];
  27. 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));
  28. [self.view addSubview:btnView];
  29. }
  30. @end














声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/428375
推荐阅读
相关标签
  

闽ICP备14008679号