首先从网上一段代码说起
.h文件
#import "father.h"
@interface Father : NSObject
@property(nonatomic,copy) NSString * name;
@end
@interface son : father
@end
---------------------------------------------------
.m文件
@implementation Father
- (instancetype)init {
if (self == [super init]) {
self.name = @"";
}
return self;
}
@end
@implementation Son
- (instancetype)init {
if (self == [super init]) {
NSLog(@"self class-->%@", [self class]);
NSLog(@"super class-->%@", [super class]);
}
return self;
}
@end
执行Son *son = [[Son alloc] init];
输出结果
2016-08-20 20:25:15.512 Test[792:28328] self class-->Son
2016-08-20 20:25:16.272 Test[792:28328] super class-->Son
对于第一段代码,我一开始以为super class打印出Father,self class打印出Son,结果输出确实2个Son,这是什么原因呢?
我们平时用OC调用对象方法如[target MethodName:var1]
,会被编译为objc_msgSend(target, @selector(MethodName:), var1)
ObjC 是一种面向runtime(运行时)的语言,也就是说,它会尽可能地把代码执行的决策从编译和链接的时候,推迟到运行时。这给程序员写代码带来很大的灵活性,比如说你可以把消息转发给你想要的对象,或者随意交换一个方法的实现之类的。这就要求 runtime 能检测一个对象是否能对一个方法进行响应,然后再把这个方法分发到对应的对象去。我们拿 C 来跟 ObjC 对比一下。在 C 语言里面,一切从 main 函数开始,程序员写代码的时候是自上而下地,一个 C 的结构体或者说类吧,是不能把方法调用转发给其他对象的。但是在oc中,我们可以在运行时把上面的target换成其他对象,非常灵活。
objc_msgSend函数的原型如下:
id objc_msgSend ( id self, SEL op, ... )
函数里2个参数
id:指向类实例的指针typedef struct objc_object *id;
其中objc_object
定义是
struct objc_object {
Class isa; // 指向对象所属类的指针
}
通过isa指针并不总能获取到对象的类别,网上的解释如下
isa指针不总是指向实例对象所属的类,不能依靠它来确定类型,而是应该用class方法来确定实例对象的类。因为KVO的实现机理就是将被观察对象的isa指针指向一个中间类而不是真实的类,这是一种叫做 isa-swizzling 的技术。
SEL:objc_msgSend函数第二个参数类型为SEL,它是selector在Objc中的表示类型(Swift中是Selector类)。selector是方法选择器,可以理解为区分方法的 ID,而这个 ID 的数据结构是SEL:typedef struct objc_selector *SEL;
objc_msgSend的函数流程:
1、先检查是否忽略selector
2、检查target是否为nil,OC允许nil执行方法
3、然后查找在这个类的IMP查找,先从cache找,找到就跳到对应函数
4、cache则去方法分发表上查找
5、本类找不到,则去父类找,一直到NSObject为止
6、如果还找不到就要开始进入动态方法解析了(这个我不懂)
上图:
isa指针
上面的objc_msgSend实现原理里面提到了isa指针、类
我们知道所有的对象都是由其对应的类实例化而来,在Objective-C中,我们用到的几乎所有类都是NSObject类的子类,NSObject类定义格式如下(忽略其方法声明)
@interface NSObject {
Class isa;
}
其中Class是typedef struct objc_class *Class;
objc_class指的又是:
struct objc_class {
Class isa;
}
为什么连续出现2个Class isa
?
对于实例对象,isa指向它所属的类,这很好理解。但是类的isa指向什么呢?要知道 类也是一个对象 ,而类所属的类别是 元类 ,类的isa指针就是指向所属的元类
实例对象-类对象-元类之间关系百度如下:
类
1.类对象的实质
类对象是由编译器创建的,即在编译时所谓的类,就是指类对象(官方文档中是这样说的: The class object is the compiled version of the class)。
任何直接或间接继承了NSObject的类,它的实例对象(instance objec)中都有一个isa指针,指向它的类对象(class object)。这个类对象(class object)中存储了关于这个实例对象(instace object)所属的类的定义的一切:包括变量,方法,遵守的协议等等。
因此,类对象能访问所有关于这个类的信息,利用这些信息可以产生一个新的实例,但是类对象不能访问任何实例对象的内容。当你调用一个 “类方法” 例如 [NSObject alloc],你事实上是发送了一个消息给他的类对象。
2.类对象和实例对象的区别
尽管类对象保留了一个类实例的原型,但它并不是实例本身。它没有自己的实例变量,也不能执行那些类的实例的方法(只有实例对象才可以执行实例方法)。然而,类的定义能包含那些特意为类对象准备的方法–类方法( 而不是的实例方法)。类对象从父类那里继承类方法,就像实例从父类那里继承实例方法一样。
类对象是一个功能完整的对象,所以也能被动态识别(dynamically typed),接收消息,从其他类继承方法。特殊之处在于它们是由编译器创建的,缺少它们自己的数据结构(实例变量),只是在运行时产生实例的代理。
元类
实际上,类对象是元类对象的一个实例!!
元类描述了 一个类对象,就像类对象描述了普通对象一样。不同的是元类的方法列表是类方法的集合,由类对象的选择器来响应。当向一个类发送消息时,objc_msgSend会通过类对象的isa指针定位到元类,并检查元类的方法列表(包括父类)来决定调用哪个方法。元类代替了类对象描述了类方法,就像类对象代替了实例对象描述了实例化方法。
很显然,元类也是对象,也应该是其他类的实例,实际上元类是根元类(root class’s metaclass)的实例,而根元类是其自身的实例,即根元类的isa指针指向自身。
类的super_class指向其父类,而元类的super_class则指向父类的元类。元类的super class链与类的super class链平行,所以类方法的继承与实例方法的继承也是并行的。而根元类(root class’s metaclass)的super_class指向根类(root class),这样,整个指针链就链接起来了!!
记住,当一个消息发送给任何一个对象, 方法的检查 从对象的 isa 指针开始,然后是父类。实例方法在类中定义, 类方法在元类和根类中定义。(根类的元类就是根类自己)。
总得来说:类对象存储实例对象的信息包括变量、实例方法,元类对象存储类对象的信息如类方法、版本号、名字
object_getClass(obj)与[obj class]的区别:
1、object_getClass(obj)返回obj的isa指针
2、[obj class]
-若obj为实例对象,调用实例方法- (Class)class,则返回obj对象中的isa指针
-若obj为类对象,调用类方法+ (Class)class,返回其本身
3、-(Class)class的实现如下:
- (Class)class {
return object_getClass(self);
}
回到代码中,对于[self class]
会被编译为
id objc_msgSend ( id self, @selector(class), ... )
消息接受者是self,自己并没有实现class的方法,然后去父类Father中寻找,也没有实现,一直到NSObjcet中找到,返回self的isa指针,self是Son的实例对象,isa指向Son,所以打印结果是Son。
[super class]
当使用到[super class]的时候会使用到objc_msgSendSuper
函数,定义如下
id objc_msgSendSuper(struct objc_super *super, @selector(class), ...)
第一个参数是个objc_super的结构体,第二个参数还是类似上面的类方法的selector,
objc_super定义:
struct objc_super {
id receiver;
Class superClass;
};
那么调用[super class]后的内部流程如下:
1.当使用 [super class] 时,这时要转换成 objc_msgSendSuper 的方法。
2.先构造 objc_super 的结构体,第一个成员变量就是 self,第二个成员变量是 father,然后要找 class 这个 selector,先去 superClass 也就是father中去找,没有,然后去father的父类中去找,结果还是在 NSObject 中找到了。
3.然后内部使用函数 objc_msgSend(objc_super->receiver, @selector(class)) 去调用,此时已经和我们用 [self class] 调用时相同了,因为此时的 receiver 还是 Son的实例对象self,所以这里返回的也是 Son。
总结:super并不是指向父类的指针,它用来告诉编译器去父类的方法列表中查到调用的方法。所以用self调用方法的时候是先从本类开始找,找不到在到父类中找;如果用super调用,则从父类开始找,找不到就从父类的父类中开始找。