super:是编译器指示符,仅仅是一个标志,并不是指针,仅仅是标志的当前对象去调用父类的方法,本质还是当前对象调用。
super的本质:其实还是当前对象去调用,只不过让当前对象去调用父类方法, super不是父类对象,指的是父类方法。
superclass:获取方法调用者的父类
class:获取方法调用者的类对象
实例对象(instance)
Student *p1 = [[Student alloc] init];
Student *p2 = [[Student alloc] init];
p1、p2是Student的instance对象(实例对象),它们是不同的两个对象,分别占据着两块不同的内存
p1、p2内存地址
instance对象在内存中存储的信息包括
类对象(Class)
NSObject *obj1 = [[NSObject alloc] init];
Class objClass1 = [obj1 class];
Class objClass2 = [NSObject class];
//class 方法返回的一直是class对象,类对象,而不是元类对象
Class objClass3 = [[NSObject class] class];
Class objClass4 = object_getClass(obj1);
NSLog(@"%p-%p-%p-%p",objClass1,objClass2,objClass3,objClass4);
class对象在内存中存储的信息包括
元类对象(meta-class)
Class metaClass = object_getClass([NSObject class]);
在内存中存储的信息主要包括
当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用
isa指向
@interface Person : NSObject
{
int _age;
int _name;
int _bro;
}
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end
@implementation Person
- (void)personInstanceMethod{
}
+ (void)personClassMethod{
}
- (id)copyWithZone:(NSZone *)zone{
return nil;
}
@end
@interface Student : Person
{
int _dog;
int _cat;
int _book;
int _phone;
int _bicycle;
int _hat;
int _clothes;
int _pants;
}
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end
@implementation Student
- (void)studentInstanceMethod{}
+ (void)studentClassMethod{}
@end
q1:上述代码各类对象superclass指向?
a1:各类的superclass指向父类对象
类对象的superclass指针指向父类对象
各类superclass指向
q2:Student实例对象调用Student实例方法的调用流程?
示例代码:
Student *stu = [[Student alloc] init];
[stu studentInstanceMethod];
a2:stu实例对象isa找到Student的类对象,调用实例方法
实例对象调用实例方法流程
q3:Student实例对象调用Person实例方法的调用流程?
示例代码:
Student *stu = [[Student alloc] init];
[stu personInstanceMethod];
a3:stu实例对象isa找到Student的类对象,通过Student的superclass找到Person的类对象,最后调用Person的实例方法实现。
stu调用Person实例方法流程
q4:Student实例对象调用init方法的调用流程?
示例代码:
Student *stu = [[Student alloc] init];
[stu init];
a3:调用流程
init
方法实现。各类元类的superclass指针指向
q1:参考上述示例,[Student studentClassMethod] 调用流程
a1:Student类isa
指针找到 Student元类,然后调用类方法 studentClassMethod
q2:参考上述示例,[Student personClassMethod] 调用流程
a3:Student类isa
指针找到 Student元类,Student元类的superclass
找到Person元类,再调用personClassMethod
q2:参考上述示例,[Student load] 调用流程
a3:Student类isa
指针找到 Student元类,Student元类的superclass
找到Person元类,Person元类的superclass
找到NSObject元类,再调用load
nil
isa、superclass指向
上述图片备注:isa指向为虚线
、superclass指向为实线
,Root class元类的superclass指向Root class的class(转弯处实线)
。可以按照Student、Person、NSObject来理解。
unrecognized selector sent to class 0x1000011**
调用方法实质是runtime的消息转发机制,runtime只会根据方法名寻找而不会在意是类方法还是实例方法。
代码验证:
#Person文件
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
+ (void)test{
NSLog(@"+[Person test] %p",self);
}
@end
#NSObject+Test文件
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
+ (void)test{
NSLog(@"+[NSObject test] %p",self);
}
@end
#调用方式:
NSLog(@"Person:%p",[Person class]);
NSLog(@"NSObject:%p",[NSObject class]);
[Person test];
[NSObject test];
输出结果1:
Person:0x10e72e090
NSObject:0x10f6d8ea8
+[Person test] 0x10e72e090
+[NSObject test] 0x10f6d8ea8
输出结果2:(如果将Person的类方法注释掉,其余代码不变)
#Person 文件
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
//+ (void)test{
// NSLog(@"+[Person test] %p",self);
//}
@end
输出结果2:
Person:0x10477b050
NSObject:0x105725ea8
+[NSObject test] 0x10477b050
+[NSObject test] 0x105725ea8
因为Person里找不到test实现方法,即找父类的实现方法
输出结果3:(如果将Person和NSObject的类方法注释掉,其余代码不变)
#Person 文件
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
//+ (void)test{
// NSLog(@"+[Person test] %p",self);
//}
@end
#NSObject+Test文件
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
//+ (void)test{
// NSLog(@"+[NSObject test] %p",self);
//}
@end
输出结果3:会报错
Person:0x10766efd0
NSObject:0x108618ea8
+[Person test]: unrecognized selector sent to class 0x10766efd0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[Person test]: unrecognized selector sent to class 0x10766efd0'
输出结果4:(如果将Person和NSObject的类方法注释掉,NSObject添加对象同名方法,其余代码不变)
#Person 文件
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
//+ (void)test{
// NSLog(@"+[Person test] %p",self);
//}
@end
#NSObject+Test文件
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
- (void)test{
NSLog(@"-[NSObject test] %p",self);
}
//+ (void)test{
// NSLog(@"+[NSObject test] %p",self);
//}
@end
输出结果4:
Person:0x101656010
NSObject:0x102600ea8
-[NSObject test] 0x101656010
-[NSObject test] 0x102600ea8
由此可见,当类方法无实现时,会调用同名的实例方法,上述辩证方法验证了isa、superclass指向图中的meta-Root class的superclass指向Root class(上图实线转弯处)。
struct xbt_objc_class {
Class isa ;
};
Person *p = [[Person alloc] init];
Class pClass = [Person class];
Class personMetaClass = object_getClass(pClass);
struct xbt_objc_class *pClass1 = (__bridge struct xbt_objc_class *)pClass;
NSLog(@"%p-%p-%p",p,pClass,personMetaClass);
LLDB 命令行查看isa值
(lldb) p p->isa//获取不到真正的isa指针
(Class) $0 = Person
(lldb) p/x (long)p->isa
(long) $1 = 0x000001a100105311//实例对象isa指针
(lldb) p/x pClass
(Class) $2 = 0x0000000100105310 Person//类对象地址
(lldb) p/x 0x000001a100105311 & 0x0000000ffffffff8//进行换算
(long) $3 = 0x0000000100105310
(lldb) p/x pClass1->isa
(Class) $0 = 0x000001a10402d2e9//类对象isa指针值
(lldb) p/x personMetaClass
(Class) $1 = 0x000000010402d2e8//元类对象isa指针值
(lldb) p/x 0x000001a10402d2e9 & 0x0000000ffffffff8//进行换算
(long) $2 = 0x000000010402d2e8
0x1c0036780-0x102a41310-0x102a412e8
结果可见:
1.实例对象isa值为0x000001a100105311
2.类对象地址为0x0000000100105310
3.换算结果:实例对象isa指针值 &0x0000000ffffffff8
才与类对象地址值相同
4.类对象isa指针为0x000001a10402d2e9
。(类并没有暴露出isa指针值,定义了一个和类相似的结构体xbt_objc_class
,进行强制转换后,才获取到类对象的isa指针)
5.元类对象地址为0x000000010402d2e8
6.换算结果:类对象isa指针值 &0x0000000ffffffff8
才与元类对象地址值相同
总结
&
ISA_MASK 值才是最终结果# if __arm64__//iPhone
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__//mac
# define ISA_MASK 0x00007ffffffffff8ULL
Q:讨论superclass指针值,是否会想isa指针值一样?
A:superclass指针值与指向对象地址相同,和isa指针值不一样
代码证明
前提:student继承自Person,写结构体来代替类
struct xht_objc_class {
Class isa ;
Class superclass ;
};
struct xht_objc_class *pClass = (__bridge struct xht_objc_class *)[Person class];
struct xht_objc_class *stuClass = (__bridge struct xht_objc_class *)[Student class];
NSLog(@"%p-%p",pClass,stuClass);
LLDB调试
(lldb) p/x stuClass->superclass
(Class) $0 = 0x0000000102299340 Person//Student类的superClass值
(lldb) p/x pClass
(xht_objc_class *) $1 = 0x0000000102299340//Person类地址
综上所述,Student类的superClass值 =
Person类地址
Q:类的本质结构
类的本质是结构体
附上runtime的结构体代码
#objc_class:objc_object
struct objc_class : objc_object {
// Class ISA;
Class superclass;
//方法缓存
cache_t cache; // formerly cache pointer and vtable
//用于获取具体类信息
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
// 诸多方法
}
#class_rw_t
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;//只读类表
method_array_t methods;//方法列表
property_array_t properties;//属性列表
protocol_array_t protocols;//协议列表
// 诸多方法
}
#class_ro_t
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;//instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;//类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;//成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
#objc_object
struct objc_object {
private:
isa_t isa;//类的isa指针是私有的
public:
// 诸多方法
}
结构体结构
想LLDB命令查看相关类结构,可以采取仿写类结构进行打印查看