-
实例对象的 isa 指向类对象,类对象的 isa 指向元类对象,元类的isa 指向基类的元类对象,基类的元类对象的 isa 指向自己
-
类对象的 superclass指向父类的类对象,如果没有父类,则 superclass 指向 nil.
-
元类对象的 superclass 指向父类的元类对象,如果没有父类(证明该类是基类),则superclass 指向类对象.
-
实例对象调用对象方法的轨迹: 对象的 isa 找到类对象,如果方法不存在,则通过类对象中的 superclass 找父类...
-
类对象调用类方法的的轨迹:类对象的 isa 找到元类对象,如果方法不存在,就通过元类对象中的 superclass 找父类...
isa 和 superclass.png -
类可以调实例方法吗?
@implementation NSObject(Test)
-(void)test{
NSLog(@"%p",self); //输出 0x1000011b8
}
@end
@interface Person : NSObject
+(void)test;
@end
@implementation Person
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"[Person class] - %p",[Person class]); //输出 0x1000011b8
[Person test];
}
return 0;
}
通过上面代码我们看到类是可以调用实例方法的.这是为什么呢?
这要从 OC 方法调用的底层说起,我们知道oc 的方法调用最后都是通过转成 objc_mesageSend()函数调用的,这个方法接收两个参数,一个是对象(实例对象或类对象),另外一个是 Selector.问题就出在这个 Selector 上,这个 selector 并没有区分是实例方法还是类方法.方法调用就根据传入对象的 isa 指针去查找方法.当我们传入的对象是实例对象时,会根据 实例对象的isa 指针找到类对象,在类对象的方法列表中查找方法,如果类对象方法列表中没有,就通过类对象的 superclass 去父类的类对象方法列表中找,直到找到为止,如果一直找到基类的类对象方法列表中都没找到,那就会报方法找不到的错误.当我们传入的对象是类对象时,会根据类对象的 isa 指针找到元类对象,在元类对象的方法类表中查找方法,如果元类对象的方法列表中没有的话,会通过元类对象的 superclass 去元类的父类对象中去找,直到找到为止,如果找到基类的元类都没找到,由于基类的元类对象的 superclass 指向基类的类对象,所以会在基类的类对象中查找方法,如果找的到就调用实例对象的该方法,如果找不到就报方法找不到的错误.所以当调用类方法时,如果没有实现该类方法,而实现了实例方法时,会调用该实例方法.
总结下来就是 oc 的方法调用顺序就是:
isa-->superclass-->superclass-->superclass...
注意 1:
这里我们需要注意基类的元类对象的 superclass 指向的是基类的类对象.
注意 2:
类只能调用基类的实例方法,如果不是基类的实例方法,同样会报unrecognized selector sent to class ***
的错入。比如下面的代码就会报这个错误
@implementation NSObject(Test)
//-(void)test{
// NSLog(@"Student = %@",self);
//}
@end
@interface Person1 : NSObject
@end
@implementation Person1
-(void)test{
NSLog(@"Student = %@",self);
}
@end
@interface Student : Person1
@end
@implementation Student
@end
@interface BigStudent : Student
+(void)test;
@end
@implementation BigStudent
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[BigStudent test];
}
return 0;
}
- 实例对象中的 isa 指向的是类对象的地址值吗?
32 位系统是的,从 64 位系统开始,isa 需要一次位运算才能计算出真实地址
实例对象的 isa & ISA_MASK = 类对象的地址值
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# endif
-
类的内部结构
类的内部结构.png -
OC 的类的信息存放在哪里?
1、成员变量的具体值,存放在实例对象中
2、对象方法、属性、成员变量、协议信息存放在类对象中
3、类方法存放在元类对象中 -
类对象和元类对象什么时候初始话,什么时候释放
类对象和元类对象在程序启动的时候就会加载进内存,在程序运行过程中始终存在,在程序终止时才释放.类对象和元类对象不论有没有使用到,都会在程序启动的时候加载进内存,类对象和元类对象在程序运行期间只有一份实例对象.
网友评论