OC底层原理:三、isa指针和superclass

583 阅读7分钟

一、isa指针

image.png 1.1 instanceisa指向class

当调用对象方法时,通过instanceisa找到class,最后找到对象方法的实现进行调用。

1.2 classisa指向meta-class

当调用类方法时,通过classisa找到meta-class,最后找到类方法的实现进行调用。

#import <Foundation/Foundation.h>

#import <objc/runtime.h>

@interface ZGPerson : NSObject<NSCopying>
{
    @public
    int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;

@end

@implementation ZGPerson

- (void)personInstanceMethod {
    
}

+ (void)personClassMethod {
    
}

- (id)copyWithZone:(NSZone *)zone {
    return  nil;
}

@end

@interface ZGStudent : ZGPerson<NSCoding>
{
    @public
    int _weight;
}
@property (nonatomic, assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;

@end

@implementation ZGStudent

- (void)studentInstanceMethod {
    
}

+ (void)studentClassMethod {
    
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    return nil;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    
}

@end

image.png

image.png

二、class对象的superclass指针

image.png 2.1 ZGStudentinstance对象调用ZGStudent的class中的对象方法流程

  • 通过 ZGStudent 的 instance对象(stu),找到 instance对象(stu)的isa
  • 根据isa找到stu的类对象ZGStudent的class
  • ZGStudent的class 中找到 对象方法(studentInstanceMethod)的实现进行调用。
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ZGStudent *stu = [[ZGStudent alloc]init];
        [stu studentInstanceMethod];
    }
    return 0;
}

2.2 ZGStudentinstance对象调用ZGPerson的class中的对象方法流程

  • 通过 ZGStudent 的 instance对象(stu),找到 instance对象(stu)的isa
  • 根据isa找到stu的类对象ZGStudent的class
  • 通过superclass找到ZGPerson的class
  • ZGPerson的class 中找到 对象方法(personInstanceMethod)的实现进行调用。
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ZGStudent *stu = [[ZGStudent alloc]init];
        [stu personInstanceMethod];
    }
    return 0;
}

2.3 ZGStudentinstance对象调用NSObject的class中的对象方法流程

  • 通过 ZGStudent 的 instance对象(stu),找到 instance对象(stu)的isa
  • 根据isa找到stu的类对象ZGStudent的class
  • 通过superclass找到ZGPerson的class
  • 通过superclass找到NSObject的class
  • NSObject的class 中找到 对象方法(init)的实现进行调用。
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ZGStudent *stu = [[ZGStudent alloc]init];
        [stu init];
    }
    return 0;
}

三、meta-class对象的superclass指针

image.png

3.1 ZGStudent类调用ZGStudent的meta-class中的类方法流程

  • 通过ZGStudent类,找到ZGStudent类的isa
  • 根据isa找到ZGStudent的meta-class
  • ZGStudent的meta-class 中找到 类方法(studentClassMethod)的实现进行调用。
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [ZGStudent studentClassMethod];
    }
    return 0;
}

3.2 ZGStudent类调用ZGPerson的meta-class中的类方法流程

  • 通过ZGStudent类,找到ZGStudent类的isa
  • 根据isa找到ZGStudent的meta-class
  • 通过superclass找到ZGPerson的meta-class
  • ZGPerson的meta-class 中找到 类方法(personClassMethod)的实现进行调用。
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [ZGStudent personClassMethod];
    }
    return 0;
}

3.2 ZGStudent类调用NSObject的meta-class中的类方法流程

  • 通过ZGStudent类,找到ZGStudent类的isa
  • 根据isa找到ZGStudentmeta-class
  • 通过superclass找到ZGPerson的meta-class
  • 通过superclass找到NSObject的meta-class
  • NSObject的meta-class 中找到 类方法(load)的实现进行调用。
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [ZGStudent load];
    }
    return 0;
}

四、isa、superclass总结

image.png 4.1 isa指向

  • instanceisa指向class
  • classisa指向meta-class
  • meta-classisa指向基类的meta-class 4.2 superclass指向
  • classsuperclass指向父类的class
    • 如果没有父类,superclass指针为nil
  • meta-classsuperclass指向父类的meta-class
    • 基类的meta-classsuperclass指向基类的class 4.3 instance调用对象方法的轨迹
  • isa找到class,方法不存在,就通过superclass父类 4.4 class调用类方法的轨迹
  • isameta-class,方法不存在,就通过superclass父类

五、isa指针新变化

image.png 从64bit开始,isa需要进行一次位运算,才能计算出真实地址。

image.png

六、类调用类方法的调用实现流程

方法调用的实质是给方法调用者发送消息,对象是方法调用者,方法名是消息名称。

[ZGPerson test]的实质就是给ZGPerson类发送一条 test消息(objc_msgSend)。下面是通过不同途径ZGPerson类调用类方法+ (void)test的实现和调用流程。

6.1 ZGPerson类类和NSObject类分别调用自己的类方法

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Test)

+ (void)test;

@end

NS_ASSUME_NONNULL_END

#import "NSObject+Test.h"

@implementation NSObject (Test)

+ (void)test {
    NSLog(@"+[NSObject test] - %p",self);
}

@end
#import <Foundation/Foundation.h>

#import <objc/runtime.h>
#import "NSObject+Test.h"

@interface ZGPerson : NSObject

+ (void)test;

@end

@implementation ZGPerson

+ (void)test {
    NSLog(@"+[ZGPerson test] - %p",self);
}

@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"ZGPerson.class---%p", ZGPerson.class);
        NSLog(@"NSObject.class---%p", NSObject.class);
        [ZGPerson test];
        [NSObject test];
    }
    return 0;
}

image.png 调用流程:

  1. 通过ZGPerson类isa找到ZGPerson的meta-class
  2. ZGPerson的meta-class中查找对应的类方法+ (void)test,找到后调用。

6.2 去掉ZGPerson类的类方法+(void)test实现

//+ (void)test {
//    NSLog(@"+[ZGPerson test] - %p",self);
//}

image.png 调用流程:

  1. 通过ZGPerson类isa找到ZGPerson的meta-class
  2. ZGPerson的meta-class中没有类方法+ (void)test,通过ZGPerson的meta-classsuperclass到它的父类NSObject的meta-class中查找类方法+ (void)test,找到后调用。

6.3 去掉NSObject+Test类的类方法+(void)test实现,改为对象方法- (void)test

#import "NSObject+Test.h"

@implementation NSObject (Test)

//+ (void)test {
//    NSLog(@"+[NSObject test] - %p",self);
//}

- (void)test {
    NSLog(@"- [NSObject test] - %p",self);
}

@end

image.png 调用流程:

  1. 通过ZGPerson类isaZGPerson的meta-class中查找对应的类方法+ (void)test
  2. ZGPerson的meta-class中没有类方法+ (void)test,通过ZGPerson的meta-classsuperclass到它的父类NSObject的meta-class中查找类方法+ (void)test
  3. NSObject的meta-class中没有类方法+ (void)test,通过NSObject的meta-classisa,到NSObject的class中查找- (void)test方法,有该方法的实现,调用成功。

七、isa指向的验证

image.png 前文我们提到,通过isa对实例对象和类对象建立联系。可以简单认为,通过实例对象的isa找到类对象class类对象classisa找到元类对象meta-class

#import <Foundation/Foundation.h>

#import <objc/runtime.h>

@interface ZGPerson : NSObject<NSCopying>
{
    @public
    int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;

@end

@implementation ZGPerson

- (void)personInstanceMethod {
    
}

+ (void)personClassMethod {
    
}

- (id)copyWithZone:(NSZone *)zone {
    return  nil;
}

@end

@interface ZGStudent : ZGPerson<NSCoding>
{
    @public
    int _weight;
}
@property (nonatomic, assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;

@end

@implementation ZGStudent

- (void)studentInstanceMethod {
    
}

+ (void)studentClassMethod {
    
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    return nil;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    
}

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ZGPerson *person = [ZGPerson new];
        Class personClass = ZGPerson.class;
        Class personMetaClass = object_getClass(personClass);
        NSLog(@"\n person:%p\n personClass:%p\n personMetaClass:%p\n",person,personClass,personMetaClass);
    }
    return 0;
}

image.png 前文我们说过,实例对象的isa指向类对象,按理来说,personisa的地址应该和person.class类对象地址是一致的,但我们发现这里的内存地址并不一样。

其实在arm64架构之前,isa就是一个普通的指针,存储着Class、MetaClass对象的内存地址。从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多信息。

image.png

64bit开始,isa需要进行一次位运算,才能计算出真实地址。

image.png 因为当前项目是Mac项目,我们调用_ _X86_64_ _架构的ISA_MASK,对应的值是0x00007ffffffffff8ULL

image.png isa & ISA_MASK 就和我们的类对象的地址对应上了。

Instance实例对象的isa & ISA_MASK地址 = class类对象地址

接下来,同样的步骤,我们来获取下元类对象的地址。

image.png 通过前文查看源码,我们了解到,我们的class内部其实是typedef* struct objc_class *Class结构,是一个结构体。那么我们写一个内部结构一模一样的结构体,指向当前的元类对象,那么我们是不是就能拿到元类对象的isa呢?

image.png

image.png

image.png 经过简化,我们得到一个这样的结构体,代码如下:

struct zg_objc_class {
    Class isa;
    Class superclass;
};

image.png

class类对象的isa & ISA_MASK地址 = meta-class元类对象地址

八、objc4源码下载

下载地址:opensource.apple.com/tarballs/ob…

image.png classmeta-class对象的本质结构都是struct objc_class

九、窥探struct objc_class的结构

objc_class:

2D013883-6A47-4B90-AD44-AF5A53E8553F.png

objc_object:

image.png isa_t:

image.png

class_rw_t:

image.png

class_ro_t:

image.png

最终我们得到了objc_class的内部结构,如下图: image.png

下面我们来验证一下这个结构能否拿取到对应的数据,拿到对应的数据,说明我们这个结构是正确的。 首先引入文件MJClassInfo.h

//
//  MJClassInfo.h
//  TestClass
//
//  Created by MJ Lee on 2018/3/8.
//  Copyright © 2018年 MJ Lee. All rights reserved.
//

#import <Foundation/Foundation.h>

#ifndef MJClassInfo_h
#define MJClassInfo_h

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif

#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;

struct bucket_t {
    cache_key_t _key;
    IMP _imp;
};

struct cache_t {
    bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
};

struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
};

struct method_t {
    SEL name;
    const char *types;
    IMP imp;
};

struct method_list_t : entsize_list_tt {
    method_t first;
};

struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    uint32_t alignment_raw;
    uint32_t size;
};

struct ivar_list_t : entsize_list_tt {
    ivar_t first;
};

struct property_t {
    const char *name;
    const char *attributes;
};

struct property_list_t : entsize_list_tt {
    property_t first;
};

struct chained_property_list {
    chained_property_list *next;
    uint32_t count;
    property_t list[0];
};

typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
    uintptr_t count;
    protocol_ref_t list[0];
};

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;
};

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t * methods;    // 方法列表
    property_list_t *properties;    // 属性列表
    const protocol_list_t * protocols;  // 协议列表
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};

#define FAST_DATA_MASK          0x00007ffffffffff8UL
struct class_data_bits_t {
    uintptr_t bits;
public:
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

/* OC对象 */
struct mj_objc_object {
    void *isa;
};

/* 类对象 */
struct mj_objc_class : mj_objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
public:
    class_rw_t* data() {
        return bits.data();
    }
    
    mj_objc_class* metaClass() {
        return (mj_objc_class *)((long long)isa & ISA_MASK);
    }
};

#endif /* MJClassInfo_h */

因为MJClassInfo.h文件是C++编写的,所以在这里需要修改一下main.m为main.mm,避免编译报错。

image.png

#import <Foundation/Foundation.h>

#import <objc/runtime.h>
#import "MJClassInfo.h"

@interface ZGPerson : NSObject<NSCopying>
{
    @public
    int _age;
    int _name;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;

@end

@implementation ZGPerson

- (void)personInstanceMethod {
    
}

+ (void)personClassMethod {
    
}

- (id)copyWithZone:(NSZone *)zone {
    return  nil;
}

@end

@interface ZGStudent : ZGPerson<NSCoding>
{
    @public
    int _weight;
}
@property (nonatomic, assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;

@end

@implementation ZGStudent

- (void)studentInstanceMethod {
    
}

+ (void)studentClassMethod {
    
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    return nil;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        struct mj_objc_class *studentClass = (__bridge struct mj_objc_class *)(ZGStudent.class);
        struct mj_objc_class *personClass = (__bridge struct mj_objc_class *)(ZGPerson.class);
        class_rw_t *studentClassData = studentClass->data();
        class_rw_t *personClassData = personClass->data();
        NSLog(@"theEnd");
       
    }
    return 0;
}

image.png

获取结果:

studentClass结构:

image.png

personClass结构:

image.png

studentClassData结构:

image.png

personClassData结构:

image.png