Runtime

作者: KeepOnline | 来源:发表于2020-08-08 07:59 被阅读0次

面试题

1.讲一下OC的消息机制

  • OC的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发一条(selector方法名)
  • objc_msgSend底层有3大阶段
    • 消息发送(当前类、父类中查找)、动态方法解析、消息转发

2.什么是Runtime?平时项目中有用过吗?

  • OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
  • OC的动态性就是有Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数
  • 平时编写的OC代码,底层都是转换成了Runtime API进行调用
  • 具体应用
    • 利用关联对象(AssociatedObject)给分类添加属性
    • 遍历类的所有成员变量(修改textfield的占位文字颜色(_placeholderLabel.textColor)、字典转模型(利用Runtime遍历所有成员的属性或者成员变量,利用KVC设值)、自动归档解档)
    • 方法交换实现(交换系统的方法)
      • class_replaceMethod method_exchangeImplementations
    • 利用消息转发机制解决方法找不到的异常问题
    • ......
3.打印结果分别是什么?
    @interface KPLStudent : KPLPerson
    @end
    
    @implementation KPLStudent
    
    - (instancetype)init {
        if (self = [super init]) {
            NSLog(@"[self class] = %@", [self class]);
            NSLog(@"[super class] = %@", [super class]);
            NSLog(@"[self superclass] = %@", [self superclass]);
            NSLog(@"[super superclass] = %@", [super superclass]);
        }
        return self;
    }
    
    @end
    
    @interface KPLPerson : NSObject
    @end
    
    BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
    BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL res3 = [[KPLPerson class] isKindOfClass:[KPLPerson class]];
    BOOL res4 = [[KPLPerson class] isMemberOfClass:[KPLPerson class]]; 
    NSLog(@"%d %d %d %d", res1, res2, res3, res4);
4.以下代码能不能执行成功?如果可以,打印结果是什么?
    @interface KPLPerson : NSObject
    @propety (copy, nonatomic) NSString *name;
    @end
    @implementation
    - (void)print {
        NSLog(@"my name's %@", self.name);
    }
    @end
    
    @implementation
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        id cls = [KPLPerson class];
        void *obj = &cls;
        [(__bridge id)obj print];
    }
    
    @end

  • Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
  • Objective-C的动态性是由Runtime API来支撑的
  • Runtime API提供的接口基本都是c语言的,源码有C\C++汇编语言编写

objc_msgSend执行流程01-消息发送

  • receiver是否为nil
    • 是(退出)
      • 从receiverClass的cache中查找方法
        • 找到了方法(调用方法结束查找)
        • 没有找到方法
          • 从receiverClass的class_rw_t中查找方法
            • 找到了方法(调用方法,结束查找,并将方法缓存到receiverClass的cache中)
            • 没有找到方法
              • a.从superClass的cache中查找方法
                • a.找到了方法(调用方法,结束查找,并将方法缓存到receiverClass的cache中)
                • a.没有找到方法(从superClass的class_rw_t中查找方法
                  • a.找到了方法(调用方法,结束查找,并将方法缓存到receiverClass的cache中)
                  • 没有找到方法(上层是否还有superClass)
                    • 有superClass,执行a
                      - 没有superClass(动态方法解析)
  • 如果是从class_rw_t中查找方法
    • 已经排序的,二分查找
    • 没有排序的,遍历查找
  • receiver通过isa指针找到receiverClass
  • receiver通过superclass指针找到superClass

objc_msgSend执行流程02-动态方法解析

  • 是否曾经有动态解析
    • 是(消息转发)
      • 调用+resolveInstanceMethod:或者+resolveClassMethod:方法来动态解析方法
        • 标记为已经动态解析
          • 消息发送
  • 开发者可以实现以下方法,来动态添加方法实现

    • +resolveInstanceMethos:
    • +resolveClassMethod:
  • 动态解析过后,会重新走"消息发送"的流程

    • "从receiverClass的cache中查找方法"这一步开始执行

动态添加方法

    + (BOOL)resolveInstanceMethos:(SEL)sel {
        if (sel == @selector(test)) {
            Method method = class_getInstanceMethod(self, @selector(other));
            class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    
    void other(id self, SEL _cmd) {
        NSLog(@"%@-%s--%s", self, sel_getName(_cmd), __func__);
    }
    
    + (BOOL)resolveClassMethod:(SEL)sel {
        if (sel == @selector(test)) {
            class_addMethod(self, sel, (IMP)other, @"v@:");
            return YES;
        }
        return [super resolveClassMethod:sel];
    }
    // Method可以理解为等价于struct method_t *
    
    
    @interface KPLPersom : NSObject
    @property (assign, nonatomic) int age;
    @end
    @implementation KPLPerson
    @dynamic age;
    @end
    
    // @dynamic是告诉编译器不用自动生成getter和setter的实现,等到运行时再添加方法实现
    

objc_msgSend的执行流程03-消息转发

  • 调用forwardingTargetForSelector:方法

    • 返回值不为nil(objc_msgSend(返回值,SEL)
    • 返回值为nil
      • 调用methodSignatureForSelector:方法
        • 方绘制不为nil(调用forwardInvocation:方法
        • 返回值为nil(调用doesNotRecognizeSelector:方法)
  • 开发者可以再forwardInvocation:方法中自定义任何逻辑

  • 以上方法都有对象方法、类方法2个版本(前面可以使加号+,也可以是减号-)

生成NSMethodSignature

    NSMethodSignature *signature = [NSMethodSignature signatureWithObjCType:"i@:i"];
    NSMethodSignature *signature = [[[KPLPerson alloc] init] methodSignatureForSelector:@selecote(test:)];

super的本质

  • super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
    • struct objc_super2
    • SEL
    struct objc_super2 {
        id receiver;
        Class current_class;
    };
  • reveiver是消息接收者
  • current_class是receiver的Class对象

相关文章

网友评论

      本文标题:Runtime

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