OC中的对象主要可以分为3种:实例对象(instance)、类对象(class)和元类对象(meta-class)
实例对象
通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
NSObject* obj1 = [[NSObject alloc] init]; NSObject* obj2 = [[NSObject alloc] init]; NSLog(@"%p %p", obj1, obj2); // 打印结果:0x600000180040 0x600000180050 从运行结果可看出以上是不同的两个实例对象,分别占据着两块不同的内存
实例对象在内存中存储的信息包括:isa指针、其他成员变量
类对象
#import Class objectClass1 = [obj1 class]; Class objectClass2 = [obj2 class]; Class objectClass3 = [NSObject class]; Class objectClass4 = object_getClass(obj1); //Runtime API Class objectClass5 = object_getClass(obj2); //Runtime API // 打印结果:0x1d6fc6070 0x1d6fc6070 0x1d6fc6070 0x1d6fc6070 0x1d6fc6070 以上都是NSObject的类对象,从运行结果可看出它们都是同一个对象,即这些指针指向的是同一块内存,每个类在内存中有且只有一个class对象
类对象在内存中存储的信息主要包括:isa指针、superclass指针、类的属性信息(@property)、类的对象方法信息(instance method)、类的协议信息(protocol)、类的成员变量(ivar,类型、名称等描述信息而不是具体的值)
元类对象
看下面如何获取元类对象(元类对象类型仍是一个类对象,底层都是struct objc_class* Class,只是包含的信息不一样)
Class objectMetaClass = object_getClass(object_getClass(obj1)); 将类对象作为参数传入,再次调用object_getClass函数
那如果调用两次class方法呢?
Class objectMetaClass2 = [[NSObject class] class]; NSLog(@"%p %p %d", objectMetaClass, objectMetaClass2, class_isMetaClass(objectMetaClass)); // 打印结果:0x1d6fc6020 0x1d6fc6070 1 0 从打印结果可以看出,class不管调多少次返回的一直是类对象,不会是元类对象
每个类只有一个元类对象,元类对象在内存中存储的信息主要包括:isa指针、superclass指针以及类方法信息
查看objc4源码
object_getClass方法中传入各种对象,通过访问isa,返回不同的类对象:
Class object_getClass(id obj) { if (obj) return obj->getIsa(); else return Nil; } // 传入类名字符串,返回对象的类对象 Class objc_getClass(const char *aClassName) { if (!aClassName) return Nil; // NO unconnected, YES class handler return look_up_class(aClassName, NO, YES); } class方法直接返回类对象:
//+ (id)self { // return (id)self; //} //- (id)self { // return self; //} + (Class)class { return self; } - (Class)class { return object_getClass(self); } //+ (Class)superclass { // return self->getSuperclass(); //} //- (Class)superclass { // return [self class]->getSuperclass(); //} 上面我们了解了对象的分类,认识到不同类型对象的差别,那么是什么让这些不同类型的对象联系起来从而构成OC对象体系的呢?
上经典老图:

isa指向链
实际上就是isa指针将它们联系起来形成 isa指向链:
instance的isa指向类classclass也有isa指向的是元类metameta中也有isa指向的是根元类root meta
当调用对象方法时,通过实例对象的isa找到class,最后找到对象方法的实现进行调用
当调用类方法时,通过类对象的isa找到meta-class,最后找到类方法的实现进行调用
类继承链
根据superclass的指向,也可总结出OC类的继承链:
nil
当Student的实例对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用
类似地,当Student的类对象要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用
Person类继承于NSObject,Student类继承于Person
@interface Person : NSObject { @public int _age; } - (void)personInstanceMethod; + (void)personClassMethod; @end @interface Student : Person { @public int _no; } - (void)studentInstanceMethod; + (void)studentClassMethod; @end 打断点,通过LLDB查看isa关联类的地址:
// 打印出实例的地址 Person* person = [Person alloc]; NSLog(@"%@", person); Student* student = [Student alloc]; NSLog(@"%@", student); 类对象的地址和实例对象
isa所指向的地址有所出入,isa需要进行一次位运算,才能计算出类对象的真实地址
在获取到对象的isa值后,可以通过&(按位与)一个掩码ISA_MASK 0x007ffffffffffff8ULL来获取到对象关联的类地址:
根据student实例的isa地址找到关联类Student的地址0x00000001000082d8

同样地,根据Student类对象的isa找到Student元类的地址0x00000001000082b0

根据Student元类对象的isa找到关联类的地址0x00000001d6fc6020

找到NSObject类对象的isa关联类地址0x00000001d6fc6020,与Student元类对象的isa关联类地址一致,可以验证元类的isa指向根元类,且根元类的isa指向自己

Class tClass = [Student class]; Class pClass = class_getSuperclass(tClass); Class nClass = class_getSuperclass(pClass); Class rClass = class_getSuperclass(nClass); NSLog(@"\n tClass-%@ \n pClass-%@ \n nClass-%@ \n rClass-%@ \n", tClass, pClass, nClass, rClass); 
可看出类对象的继承链:Student->Person->NSObject->nil
Student * student = [Student alloc]; Class tClass = object_getClass(student); Class mtClass = object_getClass(tClass); Class mtSuperClass = class_getSuperclass(mtClass); NSLog(@"\n student %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类", student, tClass, mtClass, mtSuperClass); Person * person = [Person alloc]; Class pClass = object_getClass(person); Class mpClass = object_getClass(pClass); Class mpSuperClass = class_getSuperclass(mpClass); NSLog(@"\n person %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类", person, pClass, mpClass, mpSuperClass); NSObject * obj = [NSObject alloc]; Class objClass = object_getClass(obj); Class mobjClass = object_getClass(objClass); Class mobjSuperClass = class_getSuperclass(mobjClass); NSLog(@"\n NSObject %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类 == %p NSObject类对象", obj, objClass, mobjClass, mobjSuperClass, [NSObject class]); 
可看出元类的继承链:Student Meta-class -> Person Meta-class -> NSObject Meta-class -> NSObject class -> nil
前面我们了解到了Class的类型是struct objc_class*结构体指针类型,下面就来分析一下这个结构体的定义
struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY; }; 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 // ...其他代码,objc_class定义共计531行代码... }; 继承于objc_object说明:
isasuperclass:指向父类的指针cache:缓存相关bits:用于获取具体的类信息cache_t是一个结构体
struct cache_t { private: explicit_atomic _bucketsAndMaybeMask; // 8字节 union { struct { explicit_atomic _maybeMask; // uint32_t 4字节 #if __LP64__ uint16_t _flags; // 2字节 #endif uint16_t _occupied; // 2字节 }; explicit_atomic _originalPreoptCache; // 8字节 }; }; // 此段为部分代码,cache_t定义总共有290行 分析整个cache_t的结构,发现cache_t的内存总共为16字节,后面会对其底层进行学习
在objc_class里有一段源码是data操作
class_rw_t *data() const { return bits.data(); } void setData(class_rw_t *newData) { bits.setData(newData); } data为class_rw_t类型,下面是其部分源码:

ro:成员变量、methods:方法、properties:属性、protocols协议
我们在类中定义的方法、属性等就是通过调取class_rw_t结构体中的方法获取的
下面通过实例来验证一下类的结构是否如上面一致
创建Person类继承于NSObject,定义一些属性、方法以及协议:
@protocol PersonDelegate - (void)personDelegateMethod; // 让Person类遵守并实现此协议方法 @end @interface Person : NSObject { NSString* hobby; } @property (nonatomic, strong)NSString* name; @property (nonatomic, assign)NSInteger age; - (void)sayHello; + (void)sayWorld; @end LLDB调试输出

第一个地址0x0000000100008470是类的第一个成员isa,第二个地址0x00000001d6fc6070是类的第二个成员superclassisa和superclass都是结构体指针类型,占用8字节,cache结构体占用16字节,XYPerson的地址加上8 + 8 + 16 = 32就可以得到bits的地址

相加并强转为class_data_bits_t *类型得到bits的地址0x0000000100008270,再调用data()方法就得到类型为class_rw_t的地址
调用class_rw_t的properties()方法,得到property_array_t类型的数组,继承于list_array_tt,找到list下的ptr

class property_array_t : public list_array_tt { typedef list_array_tt Super; public: property_array_t() : Super() { } property_array_t(property_list_t *l) : Super(l) { } }; ptr为property_list_t类型,继承于entsize_list_tt
struct property_list_t : entsize_list_tt { }; entsize_list_tt部分源码:
struct entsize_list_tt { uint32_t entsizeAndFlags; uint32_t count; // 数量 uint32_t entsize() const { return entsizeAndFlags & ~FlagMask; } uint32_t flags() const { return entsizeAndFlags & FlagMask; } Element& getOrEnd(uint32_t i) const { ASSERT(i <= count); return *PointerModifier::modify(*(List *)this, (Element *)((uint8_t *)this + sizeof(*this) + i*entsize())); } Element& get(uint32_t i) const { // 获取元素方法 ASSERT(i < count); return getOrEnd(i); } // ...其他代码... }; 通过调用get()方法,获取元素,下面的结果就是Person类的name、age在properties()里,而实例变量hobby不在这里

调用class_rw_t的methods()方法,得到method_array_t类型的数组,继承于list_array_tt,同样找到list下的ptr

这里看到ptr是method_list_t类型,同样继承于entsize_list_tt,其中有count为6,调用get()方法查看输出

这里的元素为method_t类型,method_t为结构体类型,其中的一个成员变量为big的结构体,里面是方法名称等信息:
struct method_t { method_t(const method_t &other) = delete; // The representation of a "big" method. This is the traditional // representation of three pointers storing the selector, types // and implementation. struct big { SEL name; const char *types; MethodListIMP imp; }; // ...其他代码 }; 调用big方法查看输出

这6个方法分别是:
sayHelloname、age的set/get方法C++析构函数:.cxx_destruct且都是实例方法,并没有类方法sayWorld
调用class_rw_t的protocols()方法,得到protocol_array_t类型的数组,继承于list_array_tt,同样找到list下的ptr

这里protocol_list_t并没有继承于entsize_list_tt:
struct protocol_list_t { // count is pointer-sized by accident. uintptr_t count; protocol_ref_t list[0]; // variable-size size_t byteSize() const { return sizeof(*this) + count*sizeof(list[0]); } protocol_list_t *duplicate() const { return (protocol_list_t *)memdup(this, this->byteSize()); } typedef protocol_ref_t* iterator; typedef const protocol_ref_t* const_iterator; const_iterator begin() const { return list; } iterator begin() { return list; } const_iterator end() const { return list + count; } iterator end() { return list + count; } }; 看到protocol_list_t的定义,我们知道count值为1,说明是有值,但是其成员是protocol_ref_t为uintptr_t类型,那怎么输出查看这个count中的1到底是什么呢

查看protocol_ref_t的定义,通过注释信息,我们可以看到protocol_ref_t未映射到protocol_t类型,那我们就找protocol_t的定义

这里看到protocol_t中有mangledName以及instanceMethods等,只要得到protocol_t就可以输出我们想要的名称方法等信息,怎么才能从protocol_ref_t映射到protocol_t呢,全局找一下吧

这里我们看到,protocol_ref_t是可以强转protocol_t的,那我们就试试:

强转成功,调用demangledName方法,我们就得到了LGPersonDelegate,那我们再找一下协议方法

按照method查看输出的步骤,成功找到协议方法personDelegateMethod
调用class_rw_t的ro方法,得到class_ro_t的结构体


查看ivars,也是继承于entsize_list_tt的ivar_list_t类型的结构体,调用get方法查看:

这6个实例变量分别是自定义hobby以及系统自动帮我们自动生成的带有_的实例变量
methods中的方法全部都存在类中,都是实例方法,那么类方法应该去在元类中找

通过类的isa指针找到元类,再根据上面的步骤找到并输出这个元类的methods
这里我们不由地想,OC的底层是
C/C++实现的,不存在对象方法和类方法的区分,有的都是函数实现,在OC的设计中,一个类可以new出无数个对象,因此把方法存在类中,而不是动态创建的对象中,是合理的。
因为OC的对象方法和类方法的定义是-和+的区分,那么方法名称就会有重名的存在,因此才会引入元类的概念,元类的存在就是解决类方法重名的问题
类的结构流程图解析:
