instance/class/metaclass类里存了什么?

2,013 阅读7分钟

前言

本文已加入专辑 彻底弄懂OC 。

概述

上一篇文章我们学习过对象本质之后,我们知道对象内部有一个isa指针,本文将描述对象的分类,学习完之后,需要我们回答以下几个问题:

  1. OC中有几种对象,它们分别是什么?
  2. 对象的isa指针指向哪里?
  3. OC的类信息存放在哪里?
  4. 对象方法的调用流程。

OC对象的分类

OC中有三种对象,我们将依次讨论,它们分别是:

  • instance 实例对象
  • class 类对象
  • metaclass 元类对象

instance

instance 是实例对象,通过alloc 出来的对象,每次调用alloc都会产生新的instance对象。

NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = [[NSObject alloc] init];
// obj1、obj2都是instance对象

obj1、obj2是NSObject的instance对象,它们是不同的两个对象,分别占据着两块不同的内存空间。

instance 对象在内存中存储的信息有:

  • isa指针,isa继承自NSObject,isa始终会排在最前面。
  • 其他成员变量

class

class是类对象,在内部中存储的信息包括:

  • isa指针
  • superClass指针
  • 类的属性信息(@property)
  • 类的对象方法信息(instance method)
  • 类的协议信息(protocol)
  • 类的成员变量信息(ivar, 描述信息)
  • ...
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对象。

meta-class

//获取元类
Class objectMetaClass = object_getClass([NSObject class]);

meta-class对象内部中存储的信息包括:

  • isa
  • superclass
  • 类方法信息(class method)
  • ...

classmeta-class的内存结构是一样的,都是Class(struct objc_class结构体)

instance、class、meta-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");

isa指针和superclass指针

了解了instance、class、metaclass的结构之后,我们看一下,isa指针和superclass指针。instance、class、metaclass内部都含有isa指针,而只有class、metaclass内部含有suerclass指针。

isa指针

  • instance的isa指向class。
  • class的isa指向metaclass。
  • metaclass的isa指向基类的metaclass。

super指针

  • class的superclass指向父类的class。
  • 基类的superclass指向nil。
  • metaclass的superclass指向父类的metaclass。
  • 基类的metaclass的superclass指向基类的class。

下面我们创建两个类PersonStudent 其中,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);

通过分析,我们得出如下结论:

  • instance调用对象方法的轨迹,通过instance的isa找到class对象,在class对象内部查找方法,找不到就通过superclass指针找到其父类,然后继续查找方法。
  • class调用类方法的轨迹,通过class的isa找到metaclass,在metaclass内部查找方法,找不到就通过superclass指针到到其父类,然后继续查找方法。

通过上面的分析,我们画出如下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]。它的调用轨迹是:

  1. 通过Person类对象的isa找到Person的metaclass对象,在其内部查找test方法,没找到。
  2. 然后通过Person metaclass的superclass指针找到其父类NSObject metaclass,在其内部找到test方法,依然找不到。
  3. 这是如果NSObject的metaclass的superclass指向为nil,test的调用应该报错。但却调用到了NSObject的class对象内部的-test方法。可见基类的metaclass的superclass指向基础的class对象。

如何通过isa找到class或者metaclass

理论上,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

通过分析日志,我们得出结论:

  • instance的isa & ISA_MASK 获取到对应的class地址。
  • class的isa & ISA_MASK 获取到对应的metaclass的地址。
  • class、metaclass的superclass地址就是其父类地址。
image-20200517171032294

class、metaclass内部结构

// 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个问题吗?想必你已经有了答案!

技术交流

技术交流