美文网首页
iOS runtime 理解

iOS runtime 理解

作者: 追着公车的少年_4934 | 来源:发表于2019-03-04 14:11 被阅读0次

    苹果开源代码下载地址
    苹果开源代码地址

    源码基于objc4-750

    runtime是什么?

    iOS对象在运行时动态创建对象和类、实现消息传递和消息转发的机制.objc_msgSend(objc,SEL);

    runtime原理?

    了解runtime原理前需要了解iOS一个Class结构。

    Class结构

    NSObject.h的定义可以知道NSObject类对象是一个Class isa。是一个objc_class结构体。

    @interface NSObject <NSObject> {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wobjc-interface-ivars"
        Class isa  OBJC_ISA_AVAILABILITY;
    #pragma clang diagnostic pop
    } 
    

    objc-runtime-new.hClass的定义是一个objc_class结构体的指针。objc_class是继承自objc_object的结构体(Objective-C 2.0).
    idobjc_object结构体的指针。

    typedef struct objc_class *Class;
    typedef struct objc_object *id;
    

    objc_class结构体在objc-runtime-new.h声明:

    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;    // 类对象的父类
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
        class_rw_t *data() { 
            return bits.data();
        }
        ... // 省略了其他代码。
        // NOT identical to this->ISA when this is a metaclass
        Class getMeta() {   // 元类
            if (isMetaClass()) return (Class)this;
            else return this->ISA(); 
        }
        
        bool isRootClass() {
            return superclass == nil;
        }
        bool isRootMetaclass() {
            return ISA() == (Class)this;
        }
    };
    

    cache是一个cache_t的结构体。结构体包含函数的IMP等一些信息,优化方法调用。

    struct cache_t {
        struct bucket_t *_buckets;
        mask_t _mask;
        mask_t _occupied;
        ...
    };
    
    struct bucket_t {
    private:
        // IMP-first is better for arm64e ptrauth and no worse for arm64.
        // SEL-first is better for armv7* and i386 and x86_64.
    #if __arm64__
        MethodCacheIMP _imp;
        cache_key_t _key;
    #else
        cache_key_t _key;
        MethodCacheIMP _imp;
    #endif
    
    public:
        inline cache_key_t key() const { return _key; }
        inline IMP imp() const { return (IMP)_imp; }
        inline void setKey(cache_key_t newKey) { _key = newKey; }
        inline void setImp(IMP newImp) { _imp = newImp; }
    
        void set(cache_key_t newKey, IMP newImp);
    };
    

    class_data_bits_t通过bits & FAST_DATA_MASK用于class_rw_t* data的运算。

    class_rw_t* data() {
            return (class_rw_t *)(bits & FAST_DATA_MASK);
        }
    

    dataclass_rw_t的结构体。

    class_rw_t结构体包含class_ro_t结构体的指针(一维数组,只读)。包含类的初始内容。方法列表method_array_t、属性列表property_array_t、协议protocol_array_t等一些属性(method_array_t是一个二维数组)。

    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize;
    #ifdef __LP64__
        uint32_t reserved;
    #endif
    
        const uint8_t * ivarLayout;
        
        const char * name;  // 类名
        method_list_t * baseMethodList;  //方法列表
        protocol_list_t * baseProtocols;  //代理协议列表
        const ivar_list_t * ivars; // 属性列表
    
        const uint8_t * weakIvarLayout;
        property_list_t *baseProperties;
    
        method_list_t *baseMethods() const {
            return baseMethodList;
        }
    };
    
    struct class_rw_t {
        // Be warned that Symbolication knows the layout of this structure.
        uint32_t flags;
        uint32_t version;
    
        const class_ro_t *ro;
    
        method_array_t methods;   // 方法列表
        property_array_t properties; // 属性列表。属性的getter、setter方法在methods中
        protocol_array_t protocols; // 协议列表
    
        Class firstSubclass;
        Class nextSiblingClass;
    
        char *demangledName;
    };
    

    objc_objectobjc_private.h中的声明:

    struct objc_object {
    private:
        isa_t isa;  
    
    public:
    
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA();
    
        // getIsa() allows this to be a tagged pointer object
        Class getIsa();
        ...
    }
    
    runtime.jpg

    结合图片和代码分析可知:

    objc_object有一个isa指针,objc_class继承自objc_object也拥有一个isa指针。类对象的isa指针指向为元类metaclass.每个类对象有个superclassisa指针。而实例对象是一个通过[[obj alloc] init]或者[obj new]出来的对象。

    实例对象(instance)的指针指向类对象(class)。类对象的指针指向根元类(metacalss)。

    根类对象rootclasssuperclassnil.根类对象的指针指向根元类root metaclass.而根元类的指针指向自己,并且根元类的superclass为根类对象。

    Method

    Method是一个method_t结构体指针。

    struct method_t {
        SEL name;   //函数名
        const char *types;  // 函数参数类型
        MethodListIMP imp;  // 指向函数实现的指针
    
        struct SortBySELAddress :
            public std::binary_function<const method_t&,
                                        const method_t&, bool>
        {
            bool operator() (const method_t& lhs,
                             const method_t& rhs)
            { return lhs.name < rhs.name; }
        };
    };
    

    SEL是一个objc_selector结构体的指针。保存了函数名的C字符串。所以在函数声明和实现时,方法名相同和参数列表一样,参数类型不一样的情况下会报错。

    /// An opaque type that represents a method selector.
    typedef struct objc_selector *SEL;
    

    IMP是一个函数指针。IMP函数指针指向了方法实现的首地址。当OC发起消息后,最终执行代码是由IMP决定。利用这个特性,当需要重复多次调用函数时,可以绕开消息绑定,直接用IMP指针掉起方法。

    /// A pointer to the function of a method implementation. 
    #if !OBJC_OLD_DISPATCH_PROTOTYPES
    typedef void (*IMP)(void /* id, SEL, ... */ ); 
    #else
    typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
    #endif
    

    Method建立了SELIMP的关联,当一个对象发送消息后会通过SEL找到对应的IMP执行。

    runtime消息传递

    objc_msgSend是用汇编实现。[obj send]在编译时会转换成objc_msgSend(obj, send)函数。

    runtime执行流程:
    1.通过objisa指针找到Class,如果是类方法则会在metaClass中查找。
    2.在class的缓存列表中查找是否有对应的方法。没有从methodList中查找。如果methodList找不到会沿着继承链一直往根部找,如果找到执行并添加到缓存中。
    3.如果找不到会触发消息转发。

    消息转发

    runtime执行流程到第3步时,触发消息转发。消息转发流程如下:

    method.png

    1.程序会调用动态解析函数resolveClassMethod:resolveInstanceMethod:(类方法和实例方法)。在当前函数中可以添加一个对应的函数实现。该函数返回已BOOL是否可以处理对应的函数,如果return NO;则执行第2步。
    2.调用forwardingTargetForSelector:备用接收者函数。这个函数可以指定其他接收者。该函数返回一个id类型的参数,如果返回nil则执行第3步。
    3.执行methodSignatureForSelector:函数。该函数需要返回一个方法签名。如果返回nil,程序异常。如果返回对应的方法签名,则调用forwardInvocation:函数响应。可以在该函数中指定函数响应的对象。

    runtime应用场景?

    1.category属性关联(通过hash表)。

    - (void)setRh_isHidden:(BOOL)rh_isHidden {
        objc_setAssociatedObject(self, rh_isShowKey, @(rh_isHidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    }
    
    - (BOOL)rh_isHidden {
        NSNumber *num = objc_getAssociatedObject(self, rh_isShowKey);
        return [num boolValue];
    }
    

    2.方法交换和添加。

    + (void)load {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            SEL systemViewWill = @selector(viewWillAppear:);
            SEL swizzViewWill = @selector(rh_viewWillAppear:);
            Method systemMethod = class_getInstanceMethod([self class], systemViewWill);
            Method swizzMethod = class_getInstanceMethod([self class], swizzViewWill);
            
            BOOL add = class_addMethod(self, systemViewWill, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
            if (add) {
                class_replaceMethod(self, swizzViewWill, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
            }else {
                method_exchangeImplementations(systemMethod, swizzMethod);
            }
        });
    }
    - (void)rh_viewWillAppear:(BOOL)animated {
        [self rh_viewWillAppear:animated];
    }
    

    3.消息转发。

    - (id)forwardingTargetForSelector:(SEL)selector {
        return _target;
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation {
        void *null = NULL;
        [invocation setReturnValue:&null];
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
        return [NSObject instanceMethodSignatureForSelector:@selector(init)];
    }
    

    4.模型和字典转换。
    获取到类的属性列表。进行转换
    5.KVO。
    运行时创建一个监听类的子类。重写监听类的setter方法在setter方法调用willChangeValueForKey:didChangeValueForKey:方法通知监听类。

    相关文章

      网友评论

          本文标题:iOS runtime 理解

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