美文网首页
类的分析、探索

类的分析、探索

作者: windy_3c22 | 来源:发表于2020-09-16 15:09 被阅读0次

类的isa探究

  • 准备研究类LGPerson类。沿用探究oc对象的思路,使用lldb先从类的内存、isa来进行观察。
    我们已经知道对象isa是指向其Class的。那Classisa右指向哪里呢。
类内存及指针地址

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

LGPerson & person

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

  • person对象中,其isa的指针地址& ISA_MASK后得到的是person的类LGPerson
  • LGPerson类中,其isa的指针地址& ISA_MASK后得到的是LGPerson类的类。
  • Apple中,称LGPerson类的类为元类
元类
  • 对象isa指向其实也是一个对象。可称之为类对象,其isa指向apple定义的元类
  • 元类系统给的,其定义、创建都由编译器自动完成,类的归属来自于元类
  • 元类类对象,每个都有一个独一无二的的元类用来存储类的相关信息
  • 元类本身是没有名称的,由于其与类相关,所以使用了同类名相同的名称

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

isa指向

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

  • 那么这个NSObject和我们内存中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
  • 实例对象(Instance of Subclass)isa指向类(class)
  • 类对象(class)isa指向元类(metal class)
  • 元类(metal class)isa指向根元类(root metal class)
  • 根元类(root metal class)isa指向其自己

注意:实例对象之间没有继承关系之间才存在继承关系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_class继承自objc_objectobjc_object结构体,拥有isa,所以
    objc_class也有isa

  • NSObject中的isa是由Class定义,Class来自objc_class,所以NSObject同样也有isa

  • NSObject实例化一个对象objc,因继承关系objc满足objc_object的特性(拥有isa)。isa表示指向,来源于objc_object

  • 所有的对象都是以objc_object为模板继承过来的。所以所有对象都拥有objc_object的特性。objc_object是当前的根对象

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);
    }
    ......//方法未全部贴出
}
  • isa:虽然显示注释掉了,但是其继承自objc_object,所以是有isa的。占8字节
  • superclassClass类型,由objc_object定义,是一个指针8字节
  • cache:结构体,内存大小有其内成员决定。结构体指针8字节

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;
  • _bucketsstruct bucket_t *类型,是结构体指针,占8字节

  • _mask的类型是mask_t,是unsigned int类型,占4字节

  • _maskAndBucketsuintptr_t,是unsigned long64位占8字节32位占4字节。使用注意所处的环境。

  • _mask_unusedmask_t类型,unsigned int类型,占4字节

  • _flags与_occupieduint16_t类型,是unsigned short,占2字节

注意: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方法。但是却没有类方法

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

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

相关文章

网友评论

      本文标题:类的分析、探索

      本文链接:https://www.haomeiwen.com/subject/jucoektx.html