iOS runtime 理解

2019-03-04  本文已影响0人  追着公车的少年_4934

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

源码基于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:方法通知监听类。

上一篇下一篇

猜你喜欢

热点阅读