美文网首页iOS面试总结
Runtime之类对象、元类对象、消息传递

Runtime之类对象、元类对象、消息传递

作者: Jimmy_L_Wang | 来源:发表于2019-06-19 23:42 被阅读0次

    类对象与元类对象的关系

    • 类对象存储实例方法列表等信息。
    • 元类对象存储类方法列表等信息。
    meta_class.jpg

    消息传递

    OBJC_EXPORT void
    objc_msgSend(void /* id self, SEL op, ... */ )
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    
    msg_send01.png
    OBJC_EXPORT void
    objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    
    /// Specifies the superclass of an instance. 
    struct objc_super {
        /// Specifies an instance of a class.
        __unsafe_unretained _Nonnull id receiver;  //接收者
    
        /// Specifies the particular superclass of the instance to message. 
    #if !defined(__cplusplus)  &&  !__OBJC2__
        /* For compatibility with old objc-runtime.h header */
        __unsafe_unretained _Nonnull Class class;
    #else
        __unsafe_unretained _Nonnull Class super_class;
    #endif
        /* super_class is the first class to search */
    };
    
    msg_send02.png

    消息传递机制

    msg_send03.png

    缓存查找

    给定值是SEL,目标值是对应bucket_t中的IMP

    缓存查找.png

    当前类中查找

    • 对于已排序好的列表,采用二分查找算法查找方法对应执行函数;
    • 对于没有排序的列表,采用一般遍历查找方法对应执行函数;

    父类逐级查找

    父类逐级查找.png

    消息转发

    消息转发.png
    + (BOOL) resolveInstanceMethod:(SEL)aSEL //动态地为实例方法的给定选择器提供实现
    {
        if (aSEL == @selector(resolveThisMethodDynamically))
        {
              class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
              return YES;
        }
        return [super resolveInstanceMethod:aSel];
    }
    

    在调用Objective-C转发机制之前调用此方法。如果respondsToSelector:instancesRespondToSelector:·被调用,则动态方法解析器有机会首先为给定的选择器提供IMP

    //返回首先应将无法识别的消息定向到的对象。
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        NSLog(@"forwardingTargetForSelector:");
        return nil;
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        if (aSelector == @selector(test)) {
            NSLog(@"methodSignatureForSelector:");
            // v 代表返回值是void类型的  @代表第一个参数类型是id,即self
            // : 代表第二个参数是SEL类型的  即@selector(test)
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        }
        else{
            return [super methodSignatureForSelector:aSelector];
        }
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        NSLog(@"forwardInvocation:");
    }
    

    Method-Swizzling

    method_swizzling.png
    #import "MethodChange.h"
    #import <objc/runtime.h>
    
    @implementation MethodChange
    
    + (void)load
    {
        //获取before方法
        Method before = class_getInstanceMethod(self, @selector(methodChangeBefore));
        //获取after方法
        Method after = class_getInstanceMethod(self, @selector(methodChangeAfter));
        //交换两个方法的实现
        method_exchangeImplementations(before, after);
    }
    
    - (void)methodChangeBefore
    {
        NSLog(@"methodChangeBefore");
    }
    
    - (void)methodChangeAfter
    {
        [self methodChangeAfter];
        NSLog(@"methodChangeAfter");
    }
    
    @end
    
        MethodChange *change = [MethodChange new];
        [change methodChangeBefore];
    
    2019-06-05 22:24:11.363371+0800 RuntimeTest[14603:26518469] methodChangeBefore
    2019-06-05 22:24:11.363471+0800 RuntimeTest[14603:26518469] methodChangeAfter
    

    动态添加方法

    performSelector:

    编译时没有这个方法,运行时确需要调用这个方法

    void testImp (void)
    {
        NSLog(@"test invoke");
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        // 如果是test方法 打印日志
        
        if (sel == @selector(test)) {
            NSLog(@"resolveInstanceMethod:");
    
            // 动态添加test方法的实现
            class_addMethod(self, @selector(test), testImp, "v@:");
    
            return YES;
    //        return NO;
        }
        else{
            // 返回父类的默认调用
            return [super resolveInstanceMethod:sel];
        }
    }
    

    动态方法解析

    @dynamic

    对属性标记为@dynamic,不需要编译器自动为我们添加getter和setter的具体实现,而是在运行时我们具体的调用了getter和setter的时候,再去为他添加具体的实现。

    相当于getter和setter是在运行时添加,而不是在编译时声明好具体的实现。

    编译型语言和动态运行时语言的区别

    • 动态运行时语言将函数决议推迟到运行时。
    • 编译时语言在编译期进行函数决议。

    [obj foo]objc_msgSend()函数之间有什么关系?

    [obj foo]在编译期处理之后就变成了objc_msgSend(obj,@selector(foo))函数,接下来就开始了objc_msgSend函数传递过程。

    runtime如何通过Selector找到对应的IMP地址的?

    首先查找当前实例所对应类对象的缓存是否有Selector对应缓存的IMP的实现。 ,如果有就把缓存中的函数返回给调用方;

    如果缓存中查找不到, 在根据当前类的方法列表去查找Selector对应的IMP的实现;

    如果当前类方法列表页没有查找到,在根据当前类的superClass指针,逐级查找父类的方法列表去去查找Selector对应的IMP的实现。

    能否向编译后的类中增加实例变量?

    不能,class_ro_t中ro代表readOnly,所以没有办法为期添加实例变量。

    可以向动态添加的类中添加实例变量,只要在调用注册类对的方法之前,去完成实例变量的添加,就是可以实现的。

    相关文章

      网友评论

        本文标题:Runtime之类对象、元类对象、消息传递

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