写在前面
本文将从源码上来详细剖析OC对象的分类、isa、superclass,带大家重新认识iOS。
苹果源码下载:
1.https://opensource.apple.com/releases/ 搜索objc4找到最新资源进行下载
2.https://opensource.apple.com/source/objc4/
3.https://opensource.apple.com/tarballs/objc4/ (这个是其他博文提到的下载路径 但是在博主这里会提示404了)
OC对象的分类
OC对象可以分为三种:instance对象(实例对象)、class对象(类对象)、meta-class对象(元类对象),我们现在来一一分析这三种对象。
实例对象
instance对象是调用alloc
的对象,每次调用alloc
都会产生新的instance对象。
NSObject *object1 = [[NSObject alloc] init]; NSObject *object2 = [[NSObject alloc] init]; NSLog(@"object1:%p,object2:%p",object1, object2);
|
如上:object1
和object2
就是instance对象,分别占据不同的内存,它们的内存地址也不一样。
NSObject
的源码结构如下:
@interface NSObject <NSObject> { Class isa; }
|
instance对象在内存中存储的信息包含:
1.isa指针
2.其他成员变量
@interface Person : NSObject
@property (nonatomic, assign) int age; @property (nonatomic, assign) CGFloat weight;
@end
@implementation Person
@end
Person *person1 = [[Person alloc] init]; person1.age = 18; person1.weight = 45.f;
Person *person2 = [[Person alloc] init]; person2.age = 20; person2.weight = 50.f; NSLog(@"person1:%p, person2:%p",person1, person2);
|
上面person1
和person2
的内存存储信息如下:

类对象
NSObject *object = [[NSObject alloc] init]; Class objectClass1 = object_getClass(object); Class objectClass2 = [object class]; Class objectClass3 = [NSObject class]; NSLog(@"objectClass1:%p, objectClass2:%p, objectClass3:%p",objectClass1, objectClass2, objectClass3);
|
如上:objectClass1
、objectClass2
、objectClass3
是同一个对象,它们的内存地址相同,这种叫做class对象。
每个类在内存里面有且仅有一个class对象。
class对象在内存中存储的信息包含:
1.isa指针
2.superclass指针
3.类的属性信息(@property)
4.类的对象方法信息(instance method)
5.类的协议信息(protocol)
6.类的成员变量信息(ivar)
……
注意:这里的成员变量信息要区别于instance对象里面的成员变量,class对象里面存的是只需要存储一份的东西,比如名称等,instance对象里面存的是具体的值,因为不同的实例对象成员变量的值是不一样的,这里大家要注意去区分。
class对象内存中存储的信息包含:

元类对象
Class metaClass = object_getClass([NSObject class]); NSLog(@"metaClass:%p",metaClass);
|
如上:metaClass
就是meta-class对象。
class对象和meta-class对象都是Class
类,所以meta-class对象里面存储的信息我们可以理解为跟class对象存储的是同样的,但是它们的用途不一样。
每个类在内存里面有且仅有一个meta-class对象。
这里大家可能会有一个疑问:为什么object_getClass
传instance对象和class对象得到的是不同的对象?
Class gdb_object_getClass(id obj) { if (!obj) return nil; return gdb_class_getClass(obj->getIsa()); }
|
从上面可以看到:是因为isa
指针,这个我们在后面会再详细解释。
object_getClass和objc_getClass
object_getClass
和objc_getClass
都是runtime
下的两个函数,区别如下:
object_getClass
传的是对象,objc_getClass
传的是字符串。
Class personCalss1 = object_getClass(person);
Class personCalss2 = objc_getClass("Person");
NSLog(@"class:%p %p",personCalss1, personCalss2);
|
+class和-class
+ (Class)class { return self; }
- (Class)class { return object_getClass(self); }
|
isa和superclass
isa和superclass的总结
这里借用网友的一张图片来做总结:

isa指针
在前面我们说到instance对象的方法是存放在class对象的内存里面,那么它们之间是如何保持这种关联的,就是通过isa
指针来保持关联的。
1.instance对象的isa
指向class对象。
2.class对象的isa
指向meta-class对象。
3.meta-class对象的isa
指向基类的meta-class对象。
如何证明instance对象的isa
指向class对象?
struct ww_objc_class { Class isa; };
- (void)viewDidLoad { [super viewDidLoad];
Person *person = [[Person alloc] init]; struct ww_objc_class *wwPerson = (__bridge struct ww_objc_class *)(person); Class personClass = [person class]; NSLog(@"---%p, %p",wwPerson->isa, personClass); }
2022-11-13 17:35:42.687432+0800 OC对象的本质[50885:15128534] ---0x10a69a540, 0x10a69a540
|
结论:Person类对象的isa
值 = Person类对象的地址值(这是一个APP项目)
注意:如果创建一个命令行项目来对比两个地址值,会发现是不相等的需要进行一次位运算才会相等。
命令uname -a
可以查看当前环境是64bit还是32bit。
superclass指针
1.instance对象没有superclass
指针。
2.class对象的superclass
指向父类 的class对象。
3.meta-class对象的superclass
指向父类的meta-class对象。
4.如果没有父类, superclass
指向nil。
5.基类meta-class对象的superclass
指向基类的class对象。
如何验证class对象的superclass
指向父类的class对象?
struct ww_objc_class { Class isa; Class superclass; };
- (void)viewDidLoad { [super viewDidLoad];
Person *person = [[Person alloc] init];
Class PersonClass = [person class]; struct ww_objc_class *wwPersonClass = (__bridge struct ww_objc_class *)(PersonClass);
Class NSObjectClass = [NSObject class]; NSLog(@"---%p, %p",wwPersonClass->superclass, NSObjectClass); }
2022-11-13 17:42:05.345430+0800 OC对象的本质[50983:15133846] ---0x7fff865a7710, 0x7fff865a7710
|
结论:Person类对象的superclass
值 = NSObject类对象的地址值
场景一
@interface Person : NSObject
@property (nonatomic, assign) int age; @property (nonatomic, assign) CGFloat weight;
- (void)run;
@end
@implementation Person
- (void)run { NSLog(@"---%s ---%@",__func__, self); }
@end
@interface Student : Person
@end
@implementation Student
@end - (void)viewDidLoad { [super viewDidLoad];
Student *student = [[Student alloc] init]; NSLog(@"---Student:%@",student); [student run]; }
2022-11-13 16:46:19.413744+0800 OC对象的本质[50121:15084736] ---Student:<Student: 0x600002127fc0> 2022-11-13 16:46:19.413912+0800 OC对象的本质[50121:15084736] ----[Person run] ---<Student: 0x600002127fc0>
|
student
调用run
方法的调用流程如下:
- 通过
student
的isa
指针找到Student
对象。
- 查找
Student
对象里面是否有run
方法。
- 通过
Student
对象的superclass
指针找到Person
对象。
- 查找
Person
对象的run
方法。
场景二
@interface NSObject (fly)
- (void)fly;
@end @implementation NSObject (fly)
- (void)fly { NSLog(@"-[NSObject fly] ---%p",self); }
@end
@interface Person : NSObject
+ (void)fly;
@end
@implementation Person
@end
- (void)viewDidLoad { NSLog(@"Person:%p",[Person class]); NSLog(@"NSObject:%p",[NSObject class]); [Person fly]; }
2022-11-13 17:17:07.265249+0800 OC对象的本质[50673:15113962] Person:0x10197f568 2022-11-13 17:17:07.265404+0800 OC对象的本质[50673:15113962] NSObject:0x7fff865a7710 2022-11-13 17:17:07.265539+0800 OC对象的本质[50673:15113962] -[NSObject fly] ---0x10197f568
|
如上:Person
调用fly
这个方法,但是Person
里面并没有实现fly
这个方法,在NSObject
的分类方法里面有一个fly
的实例方法。
调用[Person fly]
并没有发生崩溃,原因如下:
- 通过
Person
class对象的isa
指针找到Person
的meta-class对象。
- 查看
Person
的meta-class对象的内存里面是否有+fly
方法。(NO)
- 通过
Person
meta-class对象的superclass
指针找到父类NSObject
的meta-class对象。
- 查看
NSObject
的meta-class对象的内存里面是否有+fly
方法。(NO)
- 通过
NSObject
meta-class对象的superclass
指针找到NSObject
class对象。
- 在
NSObject
class对象中找到-fly
方法得以调用。