Runtime学习笔记

对象模型

新建一个类:

#import <Foundation/Foundation.h>

@interface CustomClass : NSObject

@end

@implementation CustomClass

@end

int main(int argc, char * argv[]) {
    CustomClass *customObject = [[CustomClass alloc] init];
}

在终端运行以下命令:

xcrun -sdk iphonesimulator12.0 clang -rewrite-objc CustomObject.m

.m文件被编译成C++文件,截取部分代码如下:

...
typedef struct objc_object CustomClass;
...
int main(int argc, char * argv[]) {
    CustomClass *customObject = ((CustomClass *(*)(id, SEL))(void *)objc_msgSend)((id)((CustomClass *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("CustomClass"), sel_registerName("alloc")), sel_registerName("init"));
}
...

可以看到我们定义的CustomClass被定义成objc_object结构体,在main函数里面的CustomClass *customObject其实就是struct objc_object *customObject,所以customObject的本质就是一个指向objc_object结构体的指针。

objc_object的源码:

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

可以看到objc_object有一个Class类型的isa变量,这个isa是指向该实例所属的类的。而Class其实本质就是objc_class

typedef struct objc_class *Class;

objc_class的源码:

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;

类其实也是对象,它的isa指向的类就是这个类的元类(meta-class)。元类只有一个对象,就是类对象。类方法就在类对象里面。

给一个对象发送消息,会到这个对象所属的类里面找对应的方法;给一个类发送消息,会到这个类的元类(也就是类对象所属的类)里面去找对应的方法。

而元类也是类,它也有isa指针,这个isa指针统一都直接指向NSObject的元类。而NSObject的元类的isa则指向自己。另外NSObject的元类的父类则指向NSObject。而其他子类的元类指向其父类的元类。

7276920-8f5d5a2450e2eb3e.png

objc_class除了isa和super_class之外,还有objc_ivar_listobjc_method_list分布保存属性和方法。

objc_method_list其实就是objc_method的列表。

objc_method的源码:

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
} 

SEL的定义:

typedef objc_selector * SEL;

可以理解为区分方法的 ID。

在Object-C中可以通过以下方法获得SEL:

SEL @selector(<selector)

SEL sel_registerName(const char * _Nonnull str)

IMP的定义:

typedef id (*IMP)(id, SEL, ...); 

指向最终实现程序的内存地址的指针。

消息机制

在Object-C里的函数调用其实都是发送消息,[receiver massage]会被编译成

objc_msgSend(receiver, selector)

如果有参数,则为:

objc_msgSend(receiver, selector, arg1, arg2, ...)

objc_class 里面还有一个变量:struct objc_cache *cache,它主要是为了调用的性能进行优化的。每当实例对象接收到一个消息时,它会先到cache中去找能够响应的方法,找不到再去方法列表里面找。找到之后就会保存到cache里面。已备下次再被调用。

如果对象接收到无法解读的消息,那将会调用其所属类的以下类方法:

+ (BOOL)resolveInstanceMethod:(SEL)sel

sel就是那个未知的选择子,返回的BOOL类型来表示这个类能否新增一个实例方法来处理这个选择子。所以本类有机会新增一个处理此选择子的方法。一般是在resolveInstanceMethod:方法里面调用class_addMethod方法来动态添加方法,但前提是实现代码已经提前写好了。

如果上述方法返回的是NO,那么当前接受者还有第二次机会,runtime还会调用该对象的以下方法,看能否将该消息转发给其他接受者处理:

- (id)forwardingTargetForSelector:(SEL)aSelector

如果此时还是返回nil,那么会来到完整的消息转发。runtime会调用以下方法:

 - (void)forwardInvocation:(NSInvocation *)anInvocation

anInvocation包含了那条尚未处理的消息的全部细节,包括选择子、目标以及参数。这个步骤可以修改消息的内容,比如追加参数或者改变选择子等。如果本类不处理此消息,会调用超类的同名方法。最后传到NSObject时会调用“doesNotRecognizeSelector:”抛出异常。

7276920-499d93dbbf91bd41.png

Category原理

KVO原理

参考

猜你喜欢

转载自blog.csdn.net/weixin_33704234/article/details/86813052
今日推荐