实例、类对象、元类对象之间的关系能够用下面这张经典的图来展现:bash
总结:数据结构
咱们知道若是调用类方法,会沿着元类对象的继承链依次向上查找方法的实现。ui
由于跟元类的父类是根类对象,因此若是在跟元类中没法查找到该方法的实现,会到根类对象中去查找。spa
而根类对象中的方法都是实例方法。指针
因此这意味着,若是一个类的类方法没有被实现,就会去调用它的根类(NSObject)中的同名实例方法。code
咱们用代码验证一下。cdn
为NSObject
新建一个Category
,并添加一个名为foo
的实例方法。方法的实现只是简单打印foo
字符串对象
//NSObject+Foo.h
#import <Foundation/Foundation.h>
@interface NSObject (Foo)
- (void)foo;
@end
//NSObject+Foo.m
#import "NSObject+Foo.h"
@implementation NSObject (Foo)
- (void)foo {
NSLog(@"foo foo");
}
@end
复制代码
新建一个UIView
的子类,并在这个子类的.h文件中声明一个名为foo
的类方法,但在.m文件中并不实现它。blog
// CView.h
#import <UIKit/UIKit.h>
@interface CView : UIView
+ (void)foo;
@end
// CView.m
#import "CView.h"
@implementation CView
@end
复制代码
而后调用这个类方法[CView foo];
继承
按理说咱们没有实现+ (void)foo;
程序应该崩溃。可是,实际上会调用NSObject的Category中的实例方法- (void)foo
,打印出"foo foo",很是神奇
若是上面提到的CView有这样一个初始化方法:
#import "CView.h"
@implementation CView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
NSLog(@"%@",NSStringFromClass([self class]));
NSLog(@"%@",NSStringFromClass([super class]));
}
return self;
}
@end
复制代码
问:若是调用这个初始化方法,打印的结果是什么?
要解答这个问题,咱们须要了解消息传递的过程?
咱们知道,在OC中方法调用[obj method]
会被编译器编译为objc_msgSend
,它的定义为objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
前两个参数是固定的,分别是消息的接受者和方法选择器。
那么super关键字调用方法会被转换为objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
,第一个参数为objc_super
指针
objc_super是下面的数据结构:
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
}
复制代码
其中的receiver
是类的一个实例,也就是self。
也就是说,最终消息的接受者仍是self这个实例。只不过,objc_msgSendSuper
查找方法实现的过程,是从self的父类对象开始的。
可是class方法是在根类NSObject
中定义的,因此不管是[self class]
仍是[super class]
都会调用NSObject中的实现,并且消息的接收者也是同样的,都是self这个实例。因此打印结构也是同样的,都打印self的类名。