类的分析、探索

2020-09-16  本文已影响0人  windy_3c22

类的isa探究

类内存及指针地址

从结果发现LGPerson类的isa指向的是LGPerson自己。我们知道LGPerson的实例对象personisa也是指向LGPerson的。那么两个isa的地址应该是指向了同一块内存,那么两个isa地址是否相同呢。

LGPerson & person

从图中可以到两个isa的地址是不相同。但是其两个isa都指向了LGPerson

元类

那么按照上面逻辑继续下去LGPerson类类的isa指向哪里呢。

isa指向

从上面看到对象的isa指向LGPerson 类对象isa指向元类,都是LGPerson,最终isa最终指向了一个NSObject类。

查看NSObject的信息

NSObject类的isa

NSObject类isa的指针地址和上面最后的根元类的isa指针地址相同。由此可见内存中NSObjectisa最终指向的NSObject应该是同一个。

创建多个类对象输出内存地址查看。

 Class class1 = [LGPerson class];
 Class class2 = [LGPerson alloc].class;
 Class class3 = object_getClass([LGPerson alloc]);
 NSLog(@"\n%p-\n%p-\n%p-\n%p", class1, class2, class3);
类地址

可以看出其地址是完全相同的,因此可以得出类对象内存中只存在一份,那么根元类NSObject同样在内存中只存在一个份其isa指向自己。

isa指向 isa流程图.png

注意:实例对象之间没有继承关系之间才存在继承关系NSObject继承自nil

类结构分析

在探究isa与cls关联的文章中使用Clangmain.m转化成c++文件探究对象的本质。同样用此去探究类Class的是什么。

typedef struct objc_class *Class;

c++文件可以发现Class是一个objc_class的结构体。接下来进入apple提供的objc4源码中探究Class

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;//废除

但是此定义确是已经被废除。寻找新的定义objc-runtime-new.h

新objc_class定义

内容太长了,只截了少部分objc4-781源码
在源码中搜索发现objc_object会发也有两个版本

//位于objc.h文件
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
//位于objc-private.h文件
struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();
    ....
}

结合编译的c++main.cpp的文件中显示,使用objc.h文件中定义的版本。

struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};

结合objc4源码和main.cpp底层编译发现:

objc_object & objc_class & object & NSObject & isa

类结构分析、属性列表、方法列表探索

@interface LGPerson : NSObject
{
    NSString *oldName;
}
@property (copy, nonatomic) NSString *nickName;
-(void)sayHello;
+(void)sayNo;
@end

结构分析

struct objc_class : objc_object {
    // Class ISA;//8字节
    Class superclass;//8字节
    cache_t cache; //16字节            // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ......//方法未全部贴出
}

cache内存大小计算
此处只粘贴了需要计算的部分。(用static修饰的不存在结构体内存中)

#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    // _maskAndBuckets stores the mask shift in the low 4 bits, and
    // the buckets pointer in the remainder of the value. The mask
    // shift is the value where (0xffff >> shift) produces the correct
    // mask. This is equal to 16 - log2(cache_size).
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
#else
#error Unknown cache mask storage type.
#endif

#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;

注意:cache计算内存注意宏定义的if else并不是每一个都需要。此处使用的是64位所以其内存大小8 + 4 + 2 +2 = 16

上面我们计算好了bits前面个成员所占字节。可以通过首地址平移的方法或bits属性,查看属性及方法的存储。

bits中的数据可通过指针函数*data()获取。

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

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ......
}

bits中存储的信息类型是class_rw_t。在其源码中会发现需要的的列表

class_rw_t中属性、方法列表 属性列表

从输出的结果中可以看到LGPerson类中bits中属性列表中有nickName属性,但是却只有1一个nickName属性,没有成员变量oldName

方法列表

方法列表中有一个c++析构函数实例方法和属性的setget方法。但是却没有类方法

通过结果发现,属性、实例方式其实是存储在当中的。

那么类方法是否存在其元类当中呢?成员变量有存储在哪里?下篇文章在探索。

上一篇 下一篇

猜你喜欢

热点阅读