前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于runtime

关于runtime

作者头像
honey缘木鱼
发布2019-12-26 17:40:10
5400
发布2019-12-26 17:40:10
举报
文章被收录于专栏:娱乐心理测试娱乐心理测试

一.概述

Runtime是一套C语言的API,基本是用 C 和汇编写的,封装了很多动态性相关的函数,在这里下到苹果维护的开源代码。主要是使用官方Api,解决我们框架性的需求。

Objective-C是一门动态语言。我们平时编写的OC代码,底层都是转换成了Runtime API进行调用。

动态:就是编译器在编译期可以只知道一个方法的名字,而不需要知道这个方法的实现,只有在运行期间调用该方法的时候,才根据方法名去找到对应方法的实现。

二.消息传递

Runtime的特性主要是消息(方法)传递,如果消息(方法)在对象中找不到,就进行消息转发。

当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应消息而做出不同的反应。这就是消息传递。

当执行[object doSomething]时,编译器转成消息发送objc_msgSend(object, doSomething),如果有参数则为objc_msgSend(object, doSomething,arg1,arg2,....) runtime的执行流程:

  1. 首先,通过objectisa指针找到它的 class ;
  2. classmethod listdoSomething ;
  3. 如果 class 中没到 doSomething,继续往它的 superclass 中找 ;
  4. 一旦找到 doSomething 这个函数,就去执行它的实现IMP ;

object的结构体

代码语言:javascript
复制
//对象
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

class 的结构体

代码语言:javascript
复制
//类
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;

isa:是一个指向objc_class结构体的指针,而isa 是它唯一的私有成员变量,即所有对象都有isa指针(isa位置在成员变量第一个位置)

代码语言:javascript
复制
#if !__OBJC2__
    Class _Nullable super_class      /*父类*/                   
    const char * _Nonnull name       /*类名*/                      
    long version                    /*版本信息*/                  
    long info                       /*类信息*/                       
    long instance_size              /*实例大小*/                      
    struct objc_ivar_list * _Nullable ivars     /*实例参数链表*/             
    struct objc_method_list * _Nullable * _Nullable methodLists     /*方法链表*/              
    struct objc_cache * _Nonnull cache                        /*方法缓存*/          
    struct objc_protocol_list * _Nullable protocols                 /*协议链表*/      
#endif

} OBJC2_UNAVAILABLE;

方法列表:

代码语言:javascript
复制
//方法列表
struct objc_method_list {
    struct objc_method_list *obsolete                        OBJC2_UNAVAILABLE;
    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
} 

方法

代码语言:javascript
复制
struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}

IMP

代码语言:javascript
复制
/// A pointer to the function of a method implementation.  指向一个方法实现的指针
typedef id (*IMP)(id, SEL, ...); 
#endif

在iOS的Runtime中,Method通过selector和IMP两个属性,实现了快速查询方法及实现,相对提高了性能,又保持了灵活性。

三.消息转发

如果在方法列表中找不到该方法,就进行消息转发。消息转发的三个步骤: 动态方法解析,备用接收者,完整消息转发。

消息转发

1.动态方法解析 Objective-C运行时会调用 +resolveInstanceMethod:(动态解析实例方法)或者 +resolveClassMethod:(动态解析类方法),可以提供一个函数实现,如果添加了函数并返回YES,那运行时系统就会重新启动一次消息发送的过程。

代码语言:javascript
复制
- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(gotoOpen)];
}

+(BOOL)resolveInstanceMethod:(SEL)sel{
    if(sel==@selector(gotoOpen)){
        class_addMethod([ self class], sel,(IMP)openMethod,"v@:");
        return YES;
    }
      return  [super resolveInstanceMethod:sel];
}

void openMethod(id obj,SEL _cmd){
    NSLog(@"打开");
}

总结:虽然没有实现方法gotoOpen,但是我们通过class_addMethod添加了方法openMethod,并执行openMethod这个函数的IMP。

代码语言:javascript
复制
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
Class cls: 将要给添加方法的类,传的类型 [类名  class]
SEL name: 将要添加的方法名,传的类型   @selector(方法名) 
IMP imp:实现这个方法的函数 ,传的类型   1,C语言写法:(IMP)方法名    2,OC的写法:class_getMethodImplementation(self,@selector(方法名:))
//OC写法
class_addMethod([ self class], sel,class_getMethodImplementation(self, @selector(openMethod)),"v@:");

-(void)openMethod{
    NSLog(@"打开");
}

const char *types:表示我们要添加的方法的返回值和参数

"v@:":v:是添加方法无返回值 @表示是id(也就是要添加的类) :表示添加的方法类型 @表示:参数类型

2.备用接收者 如果动态解析返回为NO,则执行forwardingTargetForSelector,尝试找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,返回非 nil 对象。否则返回 nil ,继续下面的动作。注意,这里不要返回 self ,否则会形成死循环。

代码语言:javascript
复制
- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(openMethod)];
}

+(BOOL)resolveInstanceMethod:(SEL)sel{
    return NO;
}

-(id)forwardingTargetForSelector:(SEL)sel{
    if(sel==@selector(openMethod)){
        return [Person new];
    }
    return [super forwardingTargetForSelector:sel];
}

Person.m

-(void)openMethod{
    NSLog(@"调用方法openMethod");
}

让备用的对象(Person)去响应了本身无法响应的一个SEL(openMethod)

3.完整消息转发 如果上一步返回nil,执行methodSignatureForSelector的方法手动将响应方法切换给备用响应对象。

代码语言:javascript
复制
- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(openMethod)];
}

+(BOOL)resolveInstanceMethod:(SEL)sel{
    return NO;
}

-(id)forwardingTargetForSelector:(SEL)sel{
    return nil;
}

-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
    if(sel==@selector(openMethod)){
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:sel];
}

-(void)forwardInvocation:(NSInvocation *)anInvocation
{
    //获得消息
    SEL sel = [anInvocation selector];
    //转发
    Person *person = [[Person alloc]init];
    if([person respondsToSelector:sel]){
        [anInvocation invokeWithTarget:person];
    }else
    {
        [super forwardInvocation:anInvocation];
    }
}
Person.m
-(void)openMethod{
    NSLog(@"调用方法openMethod");
}

如果拿到签名后的派发对象Person没有实现方法openMethod,则会执行doesNotRecognizeSelector。

代码语言:javascript
复制
-(void)doesNotRecognizeSelector:(SEL)aSelector{
    NSLog(@"此方法不存在");
}

我们经常遇到的Crash问题:unrecognized selector sent to instance 0x100a1c2e0'

调用该方法时,经过消息传递,消息转发都没有找到该方法的实现,程序就会崩溃。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.概述
  • 二.消息传递
  • 三.消息转发
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档