美文网首页
runtime笔记

runtime笔记

作者: 巴_鲁 | 来源:发表于2018-08-28 23:14 被阅读0次

    小喇叭: 个人笔记 个人笔记 个人笔记啦

    当在浏览器输入runtime,经过0.几秒,哇,这绝对是iOS界的热搜啊。
    
    这里作为个人笔记,没有太多关于runtime的概念解释,毕竟有官方圣经可以看
    
    runtime笔记分为:
    1. 消息转发机制 (OC && swift)
    2. 通过分类扩展属性 (OC && swift)
    3. 拦截系统方法 (OC && swift)
    4. 自动化归档 (OC && swift)
    5. swift反射 
    
    

    对象(object),类(class),方法(method)的结构体

    在obje.h runtime.h 里边找到对应的结构体定义
    /**
    对象
    */
    struct objc_object {
        Class isa  OBJC_ISA_AVAILABILITY;
    };
    /**
    类
    */
    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;
    /**
    方法列表
    */
    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;
    }                                                            OBJC2_UNAVAILABLE;
    /**
    方法
    */
    struct objc_method {
        SEL method_name                                          OBJC2_UNAVAILABLE;
        char *method_types                                       OBJC2_UNAVAILABLE;
        IMP method_imp                                           OBJC2_UNAVAILABLE;
    }
    
    

    看过这几个结构体定义,来看一下消息传递的实现

    eg: [someone doSomething]
    

    编译器转成消息发送objc_msgSend(someone, doSomething)

    1.系统首先找到消息的接收对象 someone ,然后通过someone的isa找到它的类。
    2.在它的类中查找method_list,是否有doSomething方法。
    3.没有则查找父类的method_list。
    4.找到对应的method,执行它的IMP。
    5.转发IMP的return值。
    

    再来看几个关键词

    - 类对象(objc_class)
    - 实例(objc_object)
    - 元类(Meta Class)
    - Method(objc_method)
    - SEL(objc_selector)
    - IMP
    - 类缓存(objc_cache)
    - Category(objc_category)
     
    

    类对象(objc_class)

    typedef struct objc_class *Class
    
    Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。
    通过上边的结构体,可以看到结构体里保存了指向父类的指针、类的名字、版本、实例大小、实例变量列表、方法列表、缓存、遵守的协议列表等。这个结构体存放的数据称为元数据(metadata)。
    该结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,因此我们称之为类对象,类对象在编译期产生用于创建实例对象,是单例。
    

    实例(objc_object)

    struct objc_object {
        Class isa  OBJC_ISA_AVAILABILITY;
    };
    
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    
    类对象中的元数据存储的都是如何创建一个实例的相关信息。
    

    元类(Meta Class)

    既然类对象中的元数据存储的都是如何创建一个实例的相关信息,那么类对象和类方法应该从哪里创建呢?
    就是类对象的isa指针指向的我们称之为元类(metaclass)
    
    image
    元类(Meta Class)是一个类对象的类。
    所有的类自身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。
    为了调用类方法,这个类的isa指针必须指向一个包含这些类方法的一个objc_class结构体。这就引出了meta-class的概念,元类中保存了创建类对象以及类方法所需的所有信息。
    任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。
    

    Method(objc_method)

    Method和我们平时理解的函数是一致的,就是表示能够独立完成一个功能的一段代码。

    eg: 
    - (void)doSomething {
        NSLog(@"doSomething");
    }
    

    通过开始我们看的objc_method结构体,我们已经看到了==SEL==和==IMP==,说明==SEL==和==IMP==其实都是Method的属性。

    SEL(objc_selector)

    /// An opaque type that represents a method selector.
    typedef struct objc_selector *SEL;
    
    objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    可以看到objc_msgSend函数第二个参数类型为SEL,它是selector在Objective-C中的表示类型(Swift中是Selector类)。
    selector是方法选择器,就是映射到方法的C字符串。
    

    IMP

    /// A pointer to the function of a method implementation. 
    typedef id (*IMP)(id, SEL, ...); 
    #endif
    
    就是指向最终实现程序的内存地址的指针。
    

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

    类缓存

    对于我们所写的诸多方法,可能只调用它们的一小部分,并且每次查找时,搜索所有选择器的类分派表没有意义。所以类实现一个缓存,每当你搜索一个类分派表,并找到相应的选择器,它把它放入它的缓存。所以当objc_msgSend查找一个类的选择器,它首先搜索类缓存。这是基于这样的理论:如果你在类上调用一个消息,你可能以后再次调用该消息。所以尽管OC是动态调用方法,但是runtime系统实际上非常快,接近直接执行内存地址的程序速度。
    参考: http://www.cocoachina.com/ios/20150818/13075.html

    Category(objc_category)

    一个指向分类的结构体的指针
    
    struct category_t { 
        const char *name; 
        classref_t cls; 
        struct method_list_t *instanceMethods; 
        struct method_list_t *classMethods;
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
    };
    这里说一下instanceProperties:表示Category里所有的properties,这就是我们可以通过objc_setAssociatedObject和objc_getAssociatedObject扩展实例变量的原因,不过这个和一般的实例变量是不一样的。
    

    消息转发

    image

    ==这里说一下 swift 是没有签名的那两个步骤的==,也就是图中第3块。

    进行一次发送消息会在相关的类对象中搜索方法列表,如果找不到则会沿着继承树向上一直搜索知道继承树根部(通常为NSObject),如果还是找不到并且消息转发都失败了就回执行doesNotRecognizeSelector:方法报unrecognized selector错。那么消息转发到底是什么呢?接下来将会逐一介绍最后的三次机会。

    1. 动态方法解析
    2. 备用接收者
    3. 完整消息转发

    下面看一下例子

    OC

    动态方法解析
    
    我们创建一个 God 类,在.h中声明一个方法
    -(void)dispatchOrder: (NSString *)message;
    我们不在.m中做实现话,直接[[[God alloc] init] dispatchOrder: @"I am God"];肯定是会崩溃的。
    
    在.m中
    
    首先,Objective-C运行时会根据实例方法或者类方法调用 +resolveInstanceMethod: 或者 +resolveClassMethod: 让我们去提供一个函数实现。
    如果你添加了函数并返回YES, 那运行时系统就会重新启动一次消息发送的过程。
    
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        /**
         class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
         const char * _Nullable types)
         */
        
        if (sel == @selector(dispatchOrder:)) {
            return class_addMethod(self, sel, (IMP)dispatchOrder, "v@:@");
        }
        // 如果没有我们走继承树
        return [super resolveInstanceMethod: sel];
    }
    
    void dispatchOrder(id obj, SEL _cmd, NSString *message) {
        NSLog(@"%@", message);
    }
    
    

    备用接收者(快速转发)

    如果resolve方法返回 NO ,运行时就会执行:forwardingTargetForSelector
    

    创建一个Person类,实现

    @implementation Person
    
    -(void)dispatchOrder: (NSString *)message {
        NSLog(@"%@,person", message);
    }
    
    @end
    

    在God中

    + (BOOL)resolveInstanceMethod:(SEL)sel {
        /**
         class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
         const char * _Nullable types)
         */
        
    //    if (sel == @selector(dispatchOrder:)) {
    //        return class_addMethod(self, sel, (IMP)dispatchOrder, "v@:@");
    //    }
        // 如果没有我们走继承树
        return [super resolveInstanceMethod: sel];
    }
    
    void dispatchOrder(id obj, SEL _cmd, NSString *message) {
        NSLog(@"%@", message);
    }
    
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        if (aSelector == @selector(dispatchOrder:)) {
            return [Person new];
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    

    完整消息转发 (swift不支持)

    分为2步:

    1. 方法签名
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        if (aSelector == @selector(dispatchOrder:)) {
            return [NSMethodSignature signatureWithObjCTypes: "v@:@"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    1. 消息转发
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        SEL sel = [anInvocation selector];
        Person *person = [[Person alloc] init];
        if ([person respondsToSelector:sel]) {
            [anInvocation invokeWithTarget: person];
            return;
        }
        [super forwardInvocation: anInvocation];
    }
    

    消息无法处理

    - (void)doesNotRecognizeSelector:(SEL)aSelector {
        NSLog(@"消息无法处理");
    }
    
    

    ==以上是OC的消息转发过程==

    swift

    1. 动态方法解析
    override class func resolveInstanceMethod(_ sel: Selector!) -> Bool {
            if sel == Selector(("dispatchOrder:")) {
                if let method = class_getInstanceMethod(self, #selector(God.dispatchMessage(_:))) {
                    let imp = method_getImplementation(method)
                    let type = method_getTypeEncoding(method)
                    return class_addMethod(self, Selector(("dispatchOrder:")), imp, type)
                }
            }
            return false
        }
        
        @objc func dispatchMessage(_ message: String) {
            print("\(message)")
        }
    
    1. 备用接受者
    class Person: NSObject {
        @objc func dispatchOrder(_ message: String) {
            print("\(message), person")
        }
    }
    
    override class func resolveInstanceMethod(_ sel: Selector!) -> Bool {
    //        if sel == Selector(("dispatchOrder:")) {
    //            if let method = class_getInstanceMethod(self, #selector(God.dispatchMessage(_:))) {
    //                let imp = method_getImplementation(method)
    //                let type = method_getTypeEncoding(method)
    //                return class_addMethod(self, Selector(("dispatchOrder:")), imp, type)
    //            }
    //        }
            return false
        }
        
        @objc func dispatchMessage(_ message: String) {
            print("\(message)")
        }
        
        override func forwardingTarget(for aSelector: Selector!) -> Any? {
            return Person()
        }
    
    

    相关文章

      网友评论

          本文标题:runtime笔记

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