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