上一篇文章我们学习过对象本质之后,我们知道对象内部有一个isa指针,本文将描述对象的分类,学习完之后,需要我们回答以下几个问题:
OC中有三种对象,我们将依次讨论,它们分别是:
instance 是实例对象,通过alloc 出来的对象,每次调用alloc都会产生新的instance对象。
NSObject *obj1 = [[NSObject alloc] init]; NSObject *obj2 = [[NSObject alloc] init]; // obj1、obj2都是instance对象 复制代码
obj1、obj2是NSObject的instance对象,它们是不同的两个对象,分别占据着两块不同的内存空间。
instance 对象在内存中存储的信息有:
class是类对象,在内部中存储的信息包括:
NSObject *object1 = [[NSObject alloc] init]; NSObject *object2 = [[NSObject alloc] init]; Class objectClass1 = [object1 class]; Class objectClass2 = object_getClass(object1); Class objectClass3 = [NSObject class]; Class objectClass4 = objc_getClass("NSObject"); Class objectClass5 = [object2 class]; Class objectClass6 = object_getClass(object2); NSLog(@"%p-%p-%p-%p-%p-%p", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5, objectClass6); //0x7fff9da14118-0x7fff9da14118-0x7fff9da14118-0x7fff9da14118-0x7fff9da14118-0x7fff9da14118 复制代码
通过 [object1 class]
或者 NSObject class]
或者 object_getClass(object1)
三个方法获取class对象。
//获取元类 Class objectMetaClass = object_getClass([NSObject class]); 复制代码
meta-class对象内部中存储的信息包括:
class
与meta-class
的内存结构是一样的,都是Class(struct objc_class结构体)
注意:
Class objectClass = [[NSObject class] class]; //返回的class对象,无论调用多少次class方法,返回的都是class对象。 object_getClass(obj1); //如果参数是instance对象,则返回class对象 //如果参数是class对象,则返回meta-class对象 //如果是meta-class对象,返回NSObject(基类)的meta-class对象 object_getClass([NSObject class]); //查看Class是否是meta-class BOOL isMetaClass = class_isMetaClass([NSObject class]); //返回参数对应的class对象。 Class class = objc_getClass("NSObject"); 复制代码
了解了instance、class、metaclass的结构之后,我们看一下,isa指针和superclass指针。instance、class、metaclass内部都含有isa指针,而只有class、metaclass内部含有suerclass指针。
下面我们创建两个类Person
与 Student
其中,Student
继承自Person
。
Person类
@interface Person : NSObject<NSCopying> { @public int _age; } @end @implementation Person - (void)personInstanceMethod { } + (void)personClassMethod { } - (nonnull id)copyWithZone:(nullable NSZone *)zone { return nil; } @end 复制代码
Student类
@interface Student : Person<NSCoding> @end @implementation Student - (void)studentInstanceMethod { } + (void)studentClassMethod { } - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder { return nil; } - (void)encodeWithCoder:(nonnull NSCoder *)coder { } - (nonnull id)copyWithZone:(nullable NSZone *)zone { return nil; } @end 复制代码
然后我们分析如下调用:
Student *student = [[Student alloc] init]; // 实例(instance)对象,调用类(class)方法。 // 先通过实例对象student的isa指针找到Student类对象 // 再在Student类对象内部找到studentInstanceMethod方法,并触发调用 [student studentInstanceMethod];// -> objc_msgSend(student, studentClassMethod); // 先通过实例对象student的isa指针找到Student类对象 // 再在Student类对象内部查找personInstanceMethod,没有找到。 // 通过Student类对象的superclass指针查找到Person类对象,在其内部找到personInstanceMethod方法,并触发调用 [student personInstanceMethod];// -> objc_msgSend(student, personInstanceMethod); // 类(class)对象调用类方法。 //在Student类对象内部查找studentClassMethod方法,找到并触发调用 [Student studentClassMethod]; // -> objc_msgSend([Student class], studentClassMethod); //在Student类对象内部查找personClassMethod方法,没有找到 //通过Student类对象的superclass指针,找到Person类对象, //在其内部找到personClassMethod,并触发调用。 [Student personClassMethod]; // -> objc_msgSend([Student class], personClassMethod); 复制代码
通过分析,我们得出如下结论:
通过上面的分析,我们画出如下isa与superclass的指向图,如下:
在上图中,有一条特殊的线,基类的metaclass的superclass指针指向的不是nil,而是基类的class对象。我们证实一下。
我们给NSObject对象增加一个分类,在分类中增加一个实例方法,如下:
@interface NSObject (test) - (void)test; @end @implementation NSObject (test) - (void)test{ NSLog(@"-NSObject (test)"); } @end 复制代码
通过上面的学习,我们知道-test
是在NSObject的class对象内部实现的。
然后我们实现Person类,Person类只是基础NSObject。
// // main.m // Class对象 // // Created by MrLi on 2020/5/16 // Modified by MrLi // Copyright © 2020 MrLi. All rights reserved. // #import <Foundation/Foundation.h> #import <objc/runtime.h> #import "NSObject+test.h" @interface Person : NSObject<NSCopying> @end @implementation Person @end int main(int argc, const char * argv[]) { @autoreleasepool { [Person test]; } return 0; } 复制代码
我们在main方法直接调用[Person test]
。它的调用轨迹是:
理论上,instance的isa指针指向的内容应该是class的地址,class的isa指针指向的内容应该是metaclass的地址。但其实它是通过& ISA_MASK
计算出来的。
在不同的架构下 ISA_MASK
取值不同。如下
# if __arm64__ # define ISA_MASK 0x0000000ffffffff8ULL # elif __x86_64__ # define ISA_MASK 0x00007ffffffffff8ULL 复制代码
我们看如下代码:
#import <Foundation/Foundation.h> #import <objc/runtime.h> @interface Student : Person<NSCoding> @end @implementation Student @end struct myclass { Class isa; Class superclass; }; int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[Student alloc] init]; struct myclass *stuclass = (__bridge struct myclass*)([Student class]); struct myclass *stumetaClass = (__bridge struct myclass*)object_getClass([Student class]); NSLog(@"%p-%p-%p", stu, stuclass, stumetaClass); NSLog(@"查看内存"); //断点 } return 0; } 复制代码
2020-05-17 17:02:06.481250+0800 Class对象[2250:1165673] 0x1031ab810-0x1000023e8-0x1000023c0
(lldb) p/x stu->isa
(Class) $0 = 0x001d8001000023e9 Student
(lldb) p/x 0x001d8001000023e9 & 0x00007ffffffffff8
(long) $1 = 0x00000001000023e8
(lldb) p/x stuclass->isa
(Class) $2 = 0x00000001000023c0
(lldb) p/x 0x00000001000023c0 & 0x00007ffffffffff8
(long) $3 = 0x00000001000023c0
(lldb) p/x stuclass->superclass
(Class) $4 = 0x0000000100002398 Person
(lldb) p/x [Person class]
(Class) $6 = 0x0000000100002398 Person
通过分析日志,我们得出结论:
// objc4-781 struct objc_object { Class _Nonnull isa; }; struct objc_class : objc_object { Class superclass; cache_t cache; class_data_bits_t bits; // 用户获取类的具体信息 } struct class_rw_t { uint32_t flags; uint16_t witness; explicit_atomic<uintptr_t> ro_or_rw_ext; Class firstSubclass; Class nextSiblingClass; public: // 获取ro const class_ro_t *ro() const { auto v = get_ro_or_rwe(); if (slowpath(v.is<class_rw_ext_t *>())) { return v.get<class_rw_ext_t *>()->ro; } return v.get<const class_ro_t *>(); } void set_ro(const class_ro_t *ro) { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { v.get<class_rw_ext_t *>()->ro = ro; } else { set_ro_or_rwe(ro); } } // 获取methods const method_array_t methods() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>()->methods; } else { return method_array_t{v.get<const class_ro_t *>()->baseMethods()}; } } //获取 properties const property_array_t properties() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>()->properties; } else { return property_array_t{v.get<const class_ro_t *>()->baseProperties}; } } //获取 protocols const protocol_array_t protocols() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>()->protocols; } else { return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols}; } } }; struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; // #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; }; 复制代码
至此,本文到此结束,还记得开篇的4个问题吗?想必你已经有了答案!