OC中的类是基于C/C++的结构体实现的,通过如下命令可以将OC代码转换成C/C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
从最简单的NSObject入手,使用以上命令转换如下的OC代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *object = [[NSObject alloc] init];
}
return 0;
}
在转换的cpp代码中,找到NSObject的结构体定义
typedef struct objc_class *Class;
struct NSObject_IMPL {
Class isa;
};
对比OC中NSObject的定义
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
@end
根据NSObject的结构体定义,一个NSObject只包含一个Class指针,在64位机器上,占据的空间大小应该是8个字节,其实质是成员变量占据的空间大小。
那NSObject实例对象占据的空间是多少,请看下面的代码。答案是16。
void theSizeOfNSObject() {
NSObject *object = [[NSObject alloc] init];
// 根据objc源码,class_getInstanceSize表示成员变量占据的空间大小。
NSLog(@"NSObject class_getInstanceSize: %zd", class_getInstanceSize([NSObject class]));
// 根据objc源码,malloc_size表示该实例对象占据的空间大小,最小是16
NSLog(@"NSObject alloc size: %zd", malloc_size((__bridge const void *)object));
}
查看Objc runtime的源码,找到class_getInstanceSize
和allocWithZone
的实现。
-
class_getInstanceSize
实质是实例变量占据的空间大小,并且会进行字节对齐 -
allocWithZone
中使用instanceSize
分配空间大小,空间大小至少是实例变量占据的空间,如果小于16字节,则还是会分配16字节
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
// Class's ivar size rounded up to a pointer-size boundary.
// 注意会字节对齐
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
// allocWithZone方法,会调用该方法
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
定义两个稍复杂的类。其中需要注意的是,Student中属性score
,被转换成结构体中_scroe
。
@interface Person : NSObject {
@public
int _age;
}
@end
@interface Student : Person {
@public
int _num;
}
@property (nonatomic, assign) int score;
@end
通过以上命令生成cpp文件,Person和Student的结构体如下:
// Person转换成的结构体
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
};
// Student转换成的结构体
struct Student_IMPL {
struct Person_IMPL Person_IVARS;
int _num;
int _score;
};
那Person和Student对象占据空间是多大?实际需要考虑以下两个因素:
- 结构体中成员变量的字节对齐
- 苹果分配空间的原则,字节数是16的倍数,见libmalloc源码
void theSizeOfPerson() {
Person *person = [[Person alloc] init];
person->_age = 16;
// Person的成员变量累加占据的空间是12,因为结构体字节对齐,实际是16个字节
NSLog(@"Person class_getInstanceSize: %zd", class_getInstanceSize([Person class]));
// 实例对象占据的空间大小,16个字节
NSLog(@"Person alloc size: %zd", malloc_size((__bridge const void *)person));
}
void theSizeOfStudent() {
Student *stu = [[Student alloc] init];
stu->_num = 4;
// Student的成员变量累加占据的空间是20,因为结构体字节对齐,实际是24
NSLog(@"Student class_getInstanceSize: %zd", class_getInstanceSize([Student class]));
// 根据libmalloc源码,分配给对象的空间,是16的倍数,32个字节
NSLog(@"Student alloc size: %zd", malloc_size((__bridge const void *)stu));
}
OC对象和结构体可以进行转化。
void instanceToStruct() {
Person *person = [[Person alloc] init];
person->_age = 4;
struct Person_IMPL *personImpl = (__bridge struct Person_IMPL *)person;
NSLog(@"person instance age is %d,struct personImpl age is %d", person->_age, personImpl->_age);
}
参考
苹果源码列表:https://opensource.apple.com/tarballs/
Demo地址:https://github.com/xiaoLong1010/DeepObjectiveC/tree/master/01-ObjectSize
网友评论