运行时

作者: memelook | 来源:发表于2018-01-04 19:14 被阅读9次

1.消息转发机制

1).简介

1).在编译期内,如果无法获得某些属性或者对象,它不会报错,因为可以动态插入。这时,就会启动消息转发机制,查找属性或方法
2). CALayer也采用了下面例子相识的方法,使得其成为兼容键值编码的容器类,也就是说能想里面随意添加属性,然后以键值对的形式访问

2).消息转发过程

1)第一阶段
咨询接收者所属的类,看其是否能动态添加方法,以处理当前这个方法或者属性,这叫做动态方法解析<1>
2)第二阶段
继第一阶段执行完,接收者自己无法以动态新增方法的手段来响应包含该选择子的消息,运行起系统就会请求接收者以其他手段来处理与消息相关的方法调用。首先,请接收者看看有没有其它对象能处理这条消息,这叫做备援接收者<2>。若有,则运行期系统会把消息转给那个对象。若无,则启动完整的消息转发机制<3>,运行期系统会把与消息有关的细节全部都封装在 NSInvocation对象中。

Snip20180104_1.png

<1>动态方法解析

+ (BOOL)resolveInstanceMethod:(SEL)selector
+ (BOOL)resolveInstanceClass:(SEL)selector

判断这个类的实例方法或类方法能不能处理,返回bool值
相关方法的实现代码已经写好,但只能运行的时候动态插入在类里面,常见于@dynamic属性

<2>备援接收者

- (id)forwardingTargetForSelector:(SEL)selector

若能找到备援对象,则将其返回,否则返回nil

<3>完全的消息转发

- (void)forwardInvocaion:(NSINvocation*)invocation

改变调用目标,使消息在新目标上得以调用即可。常用的实现方式为:在触发消息前,先以某种方法改变消息内容,比如追加另外一个参数,或是改变对象等

#import <Foundation/Foundation.h>
@interface EOCAutoDictonary : NSObject
@property (nonatomic, strong) NSString *string;
@property (nonatomic, strong) NSNumber *number;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) id opaqueObject;
@end

#import "EOCAutoDictonary.h"
#import <objc/runtime.h>

@interface EOCAutoDictonary()
@property (nonatomic, strong) NSMutableDictionary *backingStore;
@end

@implementation EOCAutoDictonary
@dynamic string, number, date, opaqueObject;

-(id)init
{
    if ((self = [super init])) {
        _backingStore = [NSMutableDictionary new];
    }

    return self;
}

+(BOOL)resolveInstanceMethod:(SEL)sel
{
    NSString *selectorString = NSStringFromSelector(sel);
    BOOL isGet = false;
    for (NSString *key in @[@"string",@"number",@"date",@"opaqueObject"]) {
        if ([selectorString isEqualToString:key]) {
            isGet = true;
        }
    }
    if ([selectorString hasPrefix:@"set"]) {
        //对象-方法-方法名的指针-类型编码
        class_addMethod(self, sel, (IMP)autoDictonaySetter, "v@:@");
    }else if (isGet || [selectorString hasPrefix:@"get"]){
        class_addMethod(self, sel, (IMP)autoDictonayGetter, "@@:");
    }
    
    return YES;
}

id autoDictonayGetter(id self, SEL _cmd){
    
    EOCAutoDictonary *typeSelf = (EOCAutoDictonary*)self;
    NSMutableDictionary *backingStore = typeSelf.backingStore;
    
    NSString *key = NSStringFromSelector(_cmd);
    return [backingStore objectForKey:key];
}

void autoDictonaySetter(id self, SEL _cmd, id value){
    
    EOCAutoDictonary *typeSelf = (EOCAutoDictonary*)self;
    NSMutableDictionary *backingStore = typeSelf.backingStore;
    
    NSString *slectorString = NSStringFromSelector(_cmd);
    NSMutableString *key = [slectorString mutableCopy];
    
    /*
     * example:"setOpaqueObject:"
     */
    
    /*
     * delete ':'
     */
    [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];
    /*
     * delete 'set'
     */
    [key deleteCharactersInRange:NSMakeRange(0, 3)];
    /*
     * 首字母小写
     */
    NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar];
    
    if (value) {
        [backingStore setObject:value forKey:key];
    }else{
        [backingStore removeObjectForKey:key];
    }
}

@end

2.消息传递机制

要调用得函数直到运行期才能确定,证明了OC是一门真正得动态语言:

void doThing(int type){
    void (*func)();
    if(type == 0 ){
        func = printA;
    }else{
        func = printB;
    }
    func();
}

OC底层调用方法得函数得原型

void objc_msgSend(id self, SEL sel, ....arg)

在运行期执行doThing,会翻译为objc_msgSend函数,objc_msgSend向self传递消息执行sel,如果self没有该方法,就会沿着继承体系继续向上查找,等查找合适得方法之后再跳转。如果最终招不到,就会消息转发。
调用一次objc_msgSend是需要很多步骤费时得,所以它会将匹配结果缓存在快速映射表里面,每个累都有一块缓存。当然这种缓存还是不如静态绑定得函数调用操作快,不过差距不大

3.方法调配技术

#import <objc/message.h>
    Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
    Method swappedMethod = class_getInstanceMethod([NSString class], @selector(eoc_myLowercaseString));
    method_exchangeImplementations(originalMethod, swappedMethod);

@interface NSString (EOCMyAdditions)
-(NSString *)eoc_myLowercaseString;
@end

@implementation NSString (EOCMyAdditions)
-(NSString *)eoc_myLowercaseString{
    
    NSString *lowercase = [self eoc_myLowercaseString];
    NSLog(@"%@ == %@",self,lowercase);
    return lowercase;
}
@end

相关文章

  • Android - base - 运行时权限获取

    Android 6.0 引入的运行时权限机制 大纲 运行时权限机制简介 在程序运行时申请权限 #运行时权限机制简介...

  • iOS RunTime分析

    1、什么是RunTime RunTime即运行时,objective-c是一种运行时的语言,什么是运行时呢,运行时...

  • Runtime

    1、什么是Runtime(运行时-机制)? Runtime简称运行时,OC就是运行时机制,也就是在程序运行时的一些...

  • 一、 运行时基本认识

    1. 什么是运行时 2. 运行时的应用场景 3. 如何应用运行时? 4. 运行时常用的函数

  • Java-Exception

    1、运行时异常和非运行时异常 运行时异常: 都是RuntimeException类及其子类异常: IndexOut...

  • Android开发异常容错处理

    Exception Java的异常分两类,运行时异常RuntimeException和非运行时异常。 运行时异常包...

  • 2019-08-15 Java的异常

    一、异常继承树 二、运行时异常与非运行时异常的区别 运行时异常:(非检查异常)运行时才可能出现的异常,显式thro...

  • 多态

    1. 什么是运行时多态? 运行时多态或动态多态是运行时存在的多态。 如果方法被重写,则在运行时将调用哪个方法是未知...

  • 【Susen】目录

    Android运行时权限Android运行时权限列表EasyPermissionsAndPermissionPer...

  • runtime运行时初探(消息机制原理)

    运行时简介: runtime简称运行时,oc就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。对...

网友评论

      本文标题:运行时

      本文链接:https://www.haomeiwen.com/subject/qckdnxtx.html