一、什么是类
字面上看,类即Class。
typedef struct objc_class *Class
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 *data() {
return bits.data();
}
......
}
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
}
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
......
}
由Objective-C objc-runtime-new.h里的代码可以知道Class是一个结构体,该结构体继承自objc_object,objec_object也就是我们所说的对象。那么我们可以得出结论:类是一个结构体,也是一个对象。这也从侧面印证了万物皆对象。
二、类的结构
类的主要结构如下:
显而易见:
- 第一个元素是一个隐藏元素(来自于父类),该属性即为isa,其指向元类;
- 第二个元素是Classsuperclass,指向其父类;
- 第三个元素是cache_t cache,类的相关缓存;
- 第四个元素是class_data_bits_t bits,返回类存储的相关信息;
- 同样我们也可以从class_rw_t *data()方法返回这些信息。
现在我们分析一下objc_clas的结构所占的内存:
- Class isa 是一个指针,占用8字节;
- Class superclass 同样是一个指针,占用8字节;
- cache_t 是一个结构体,其中第一个元素是一个指针占8字节,第二、三个元素都是int32各占4字节,总共占16字节
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
通过在在lldb中使用x/4gx 打印的地址:
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x00000001003da260 0x0000000000000000
其中0x001d800100002389就是isa的地址,因为我们已经通过上面的计算得到了前三个元素所占用的内存,根据内存偏移,我们可以得到class_data_bits_t bits的地址为0x1000023b0 + 8 + 8 + 16 = 0x1000023b0,接着分析:
通过以上在llbd的分析,我们可以看到我们给类定义的属性存在的位置如下:
class_rw_t -> (class_ro_t *)ro -> (property_list_t)baseProperties
我们自定义的成员变量,系统给属性生成的变量(_开头命名)存储的位置如下:
class_rw_t -> (class_ro_t *)ro -> (ivar_list_t)ivars
系统给属性生成的getter、setter方法和我们自定义的实例方法存储的位置如下:
class_rw_t -> (class_ro_t *)ro -> (method_list_t)baseMethodList
我们自定义的类方法存储在元类里。
1、isa
isa是一个名为isa_t的联合体,主要属性为Class cls和 uintptr_t bits。是实例对象、类、元类的桥梁。我么可以通过(Class)(isa.bits & ISA_MASK)获取到当前的类。
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
在arm64下isa的内存结构如下:
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
在x86_64下:
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
2、superclass
superclass指向类的父类,是继承的桥梁。
3、cache_t cache
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
};
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
下一节分析
4、class_data_bits_t bits
class_data_bits_t是一个结构体,大致结构如下:
struct class_data_bits_t {
uintptr_t bits;
private:
bool getBit(uintptr_t bit) {
return bits & bit;
}
public:
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
......
};
其包含一个名为class_rw_t的结构体指针,该结构体指针指向了存储了类的method、property、protocol等相关信息的内存:
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
void setFlags(uint32_t set) {
OSAtomicOr32Barrier(set, &flags);
}
void clearFlags(uint32_t clear) {
OSAtomicXor32Barrier(clear, &flags);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear) {
assert((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
};
在class_ro_t这个结构体中
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#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;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
tips:
类、元类创建的时机:编译期
tips:
当我们计算结构体大小的时候,不计算函数,只计算属性,函数存储的位置不一样。
tips:内存偏移
LGPerson *p1 = [LGPerson alloc];
LGPerson *p2 = [LGPerson alloc];
NSLog(@"--p1--%@----%p", p1, &p1);
NSLog(@"--p2--%@----%p", p2, &p2);
得到结果如下:
--p1------0x7ffeefbff508
--p2------0x7ffeefbff500
我们可以看出p1、p2的内存地址 和 指针地址都是不相同的。
int c[4] = {1, 2, 3, 4};
int *d = c;
NSLog(@"%p--%p--%p--%p", &c, &c[0], &c[1], &c[2]);
NSLog(@"%p--%p--%p",d,d+1,d+2);
0x7ffeefbff480--0x7ffeefbff480--0x7ffeefbff484--0x7ffeefbff488
0x7ffeefbff480--0x7ffeefbff484--0x7ffeefbff488
for (int i = 0; i<4; i++) {
int value1 = c[i];
int value2 = *(d+i);
NSLog(@"value1=%d==value=%d",value1, value2);
}
value1=1==value=1
value1=2==value=2
value1=3==value=3
value1=4==value=4
由打印的结果可以看出来 &c[0], &c[1],&c[2]的地址是递增的,间隔一个数组元素所占的内存大小;而由d,d+1,d+2可以看出来,地址指针加1就相当于内存加一个元素的大小。我们可以利用内存的偏移来反向得到内存地址中所存储的对象。