对象模型
新建一个类:
#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。而其他子类的元类指向其父类的元类。
objc_class除了isa和super_class之外,还有objc_ivar_list
和objc_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:”抛出异常。
Category原理
KVO原理
参考
网友评论