类的原理分析(上)

117 阅读3分钟

类的原理分析

我们首先创建一个类LKPerson,然后

LKPerson *p = [LKPerson alloc];

进入llvm调试,打印当前对象地址

image.png 0x01000001000082e9isa的地址,可以看到去除掩码之后,地址指向类LKPerson, 尝试打印类LKPerson的地址,得到

image.png 发现类的isa仍然指向的是类LKPerson,继续打印

image.png 发现这个类的isa指向了NSObject,接着打印

image.png 发现NSObjectisa指向自己。 这里为什么会有两个LKPerson,是否一个类可以有多个指针地址,继续尝试

    Class class1 = [LKPerson class];
    Class class2 = [LKPerson alloc].class;
    Class class3 = object_getClass([LKPerson alloc]);
    Class class4 = [LKPerson alloc].class;
    NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);

输出结果

image.png 发现类的地址都一样,那么为什么会有两个不一样的地址都指向LKPerson,利用MachOView工具打开Products文件,搜索LKPerson,可以看到

image.png 系统自己创建了一个METACLASS

总结 对象的isa指向类,类的isa指向元类,元类的isa指向根元类NSObject,根元类isa指向根元类NSObject

类的继承链

创建一个类LKTeacher继承自LKPerson

    Class psClass = class_getSuperclass(LKPerson.class);//LKPerson的父类
    Class pMetaClass = object_getClass(LKPerson.class);//LKPerson元类
    Class psuperClass = class_getSuperclass(pMetaClass);//LKPerson元类的父类
    NSLog(@"LKPerson类的父类 %@ - %p",psClass,psClass);
    NSLog(@"LKPerson元类 %@ - %p",pMetaClass,pMetaClass);
    NSLog(@"LKPerson元类的父类 %@ - %p",psuperClass,psuperClass);
    
    Class tMetaClass = object_getClass(LKTeacher.class);//LKTeacher元类
    Class tsuperClass = class_getSuperclass(tMetaClass);//LKTeacher元类的父类
    NSLog(@"LKTeacher元类的父类 %@ - %p",tsuperClass,tsuperClass);
    
    Class nsuperClass = class_getSuperclass(NSObject.class);//NSObject的父类
    NSLog(@"NSObject的父类%@ - %p",nsuperClass,nsuperClass);
    
    Class nMetaClass = object_getClass(NSObject.class);// 根元类NSObject
    Class rnsuperClass = class_getSuperclass(nMetaClass);//根元类NSObject的父类
    NSLog(@"NSObject类%@ - %p",NSObject.class,NSObject.class);
    NSLog(@"NSObject元类%@ - %p",nMetaClass,nMetaClass);
    NSLog(@"NSObject元类的父类%@ - %p",rnsuperClass,rnsuperClass);

输出结果

image.png 可以看到LKPerson元类的父类是根元类NSObjectLKTeacher元类的父类是LKPerson的元类,NSObject的父类为nilNSObject的元类的父类为NSObject

总结 类->父类->NSObject->nil 元类->父类的元类->根元类NSObject->NSObject->nil

isa流程图

isa流程图.png

根据源码和llvm分析类的结构

新建一个类LKPerson

@interface LKPerson : NSObject{
    NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;
@property (nonatomic, assign) int count;

- (void)sayNB;
- (void)sayNB:(NSString *)str count:(int)count;
+ (void)say666;
@end

类的本质是一个结构体objc_class

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // 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
    
    //后面省略
}

可以看到第一个为isa为8字节,superclass为8字节,

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;
#if __LP64__
            uint16_t                   _flags;
#endif
            uint16_t                   _occupied;
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
    
    //后面省略,结构体大小只与成员变量有关,static存储在全局区,所以省略
}

从上面可以看出cache_t为8+8=16字节。
利用llvm打印,得到LKPerson首地址 image.png 偏移8+8+16个字节后,得到我们想要的bits,打印 image.png 查看class_data_bits_t,可以看到data方法

    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

查看class_rw_t

    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_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }

    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_or_rw_ext)->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }

    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 *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
        }
    }

    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 *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
        }
    }

    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 *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }

打印

image.png 可以看到,声明的属性都存放在properties里面,但是subject并不在里面。 接下来我们试着打印方法

image.png 用同样的方法打印不出来,查看method_t

    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };

private:
    bool isSmall() const {
        return ((uintptr_t)this & 1) == 1;
    }

    // The representation of a "small" method. This stores three
    // relative offsets to the name, types, and implementation.
    struct small {
        // The name field either refers to a selector (in the shared
        // cache) or a selref (everywhere else).
        RelativePointer<const void *> name;
        RelativePointer<const char *> types;
        RelativePointer<IMP> imp;

        bool inSharedCache() const {
            return (CONFIG_SHARED_CACHE_RELATIVE_DIRECT_SELECTORS &&
                    objc::inSharedCache((uintptr_t)this));
        }
    };

可以看到bigsmall两个结构体,还可以找到

    objc_method_description *getDescription() const {
        return isSmall() ? getSmallDescription() : (struct objc_method_description *)this;
    }

打印结果 image.png 可以看到,getset方法,以及实例方法,但是并没有类方法

探索变量以及类方法

查看ro,找到ivars打印 image.png

类方法存储在元类里面 image.png