类的isa探究
- 准备研究类
LGPerson
类。沿用探究oc对象的思路,使用lldb
先从类的内存、isa
来进行观察。
我们已经知道对象isa
是指向其Class
的。那Class
的isa
右指向哪里呢。
从结果发现LGPerson
类的isa
指向的是LGPerson
自己。我们知道LGPerson
的实例对象person
的isa
也是指向LGPerson
的。那么两个isa
的地址应该是指向了同一块内存
,那么两个isa
地址是否相同呢。
从图中可以到两个isa
的地址是不相同。但是其两个isa
都指向了LGPerson
。
- 在
person
对象中,其isa
的指针地址& ISA_MASK
后得到的是person
的类LGPerson
。 - 在
LGPerson
类中,其isa
的指针地址& ISA_MASK
后得到的是LGPerson类
的类。 - 在
Apple
中,称LGPerson类
的类为元类
。
元类
对象
的isa
指向类
,类
其实也是一个对象
。可称之为类对象
,其isa
指向apple
定义的元类
。元类
是系统
给的,其定义、创建
都由编译器
自动完成,类的归属
来自于元类
。元类
是类对象
的类
,每个类
都有一个
独一无二的的元类
用来存储类的相关信息
。元类
本身是没有名称
的,由于其与类
相关,所以使用了同类名相同的名称
。
那么按照上面逻辑继续下去LGPerson类
的类的isa
指向哪里呢。
从上面看到对象的isa
指向类
,LGPerson 类对象
的isa
指向元类
,都是LGPerson
,最终isa
最终指向了一个NSObject
类。
- 那么这个
NSObject
和我们内存中NSObject
一样吗。 -
类对象
在内存
中又存在多少呢。
查看NSObject的信息
NSObject类的isaNSObject类
的isa的指针地址
和上面最后的根元类的isa指针地址
相同。由此可见内存中NSObject
和isa
最终指向的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
指向自己。
实例对象(Instance of Subclass)
的isa
指向类(class)
类对象(class)
的isa
指向元类(metal class)
元类(metal class)
的isa
指向根元类(root metal class)
根元类(root metal class)
的isa
指向其自己
注意:实例对象
之间没有继承关系
。类
之间才存在继承关系
。NSObject
继承自nil
。
类结构分析
在探究isa与cls关联的文章中使用Clang
将main.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
内容太长了,只截了少部分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_object
。objc_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
是当前的根对象
。
类结构分析、属性列表、方法列表探索
@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字节
。 -
superclass
:Class
类型,由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;
-
_buckets
是struct bucket_t *
类型,是结构体指针
,占8字节
。 -
_mask
的类型是mask_t
,是unsigned int
类型,占4字节
。 -
_maskAndBuckets
是uintptr_t
,是unsigned long
,64位占8字节
,32位占4字节
。使用注意所处的环境。 -
_mask_unused
是mask_t
类型,unsigned int
类型,占4字节
。 -
_flags与_occupied
是uint16_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
。在其源码中会发现需要的的列表
从输出的结果中可以看到LGPerson
类中bits
中属性列表中有nickName
属性,但是却只有1一个nickName
属性,没有成员变量oldName
。
方法列表中有一个c++
的析构函数
和实例方法
和属性的set
和get
方法。但是却没有类方法
。
通过结果发现,属性、实例方式
其实是存储在类
当中的。
那么类方法是否存在其元类当中呢?成员变量有存储在哪里?下篇文章在探索。
网友评论