runtime笔记

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

当在浏览器输入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)
runtime笔记_第1张图片
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扩展实例变量的原因,不过这个和一般的实例变量是不一样的。

消息转发

runtime笔记_第2张图片
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笔记)