(本文所运行的Demo是在集成了objc4源码基础上的,详见:gitHub)
本文所写的项目详见:OCBasicDemo
从前面的《OC对象的本质(一)》我们了解了为一个NSObject对象系统到底分配了多少的内存
接下来我们再来分析下:自定义对象 内存,底层到底是怎么实现的.
我们自定义一个Student类如下:
#import <Foundation/Foundation.h>
@interface Student : NSObject{
@public
int age;
int number;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Start");
Student *stu = [[Student alloc]init];
stu->age = 18;
stu->number = 1;
NSLog(@"End");
}
return 0;
}
使用终端:进入main.m 我们将它转为C/C++代码:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o myMain.cpp
我们在myMain.cpp里面发现
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int age;
int number;
};
NSObject_IMPL为:
struct NSObject_IMPL {
Class isa;
};
下面我来用代码来验证下:
#import <Foundation/Foundation.h>
@interface Student : NSObject{
@public
int age;
int number;
}
@end
@implementation Student
@end
struct Student_IMPL {
Class isa;
int age;
int number;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Start");
Student *stu = [[Student alloc]init];
stu->age = 18;
stu->number = 1;
//我们强转下
struct Student_IMPL *stu2 = (__bridge struct Student_IMPL *)(stu);
NSLog(@"%d<--->%d",stu2->age,stu2->number);//18 1
NSLog(@"%@",stu);//0x100f22bb0
NSLog(@"%p",&(stu2->isa));//0x100f22bb0
NSLog(@"%p",&(stu->age));//0x100f22bb8
NSLog(@"%p",&(stu2->age));//0x100f22bb8
NSLog(@"%zd", class_getInstanceSize([Student class])); //16个字节
NSLog(@"%zd", malloc_size((__bridge const void *)stu)); //16个字节
NSLog(@"End");
}
return 0;
}
分析:
打印stu地址和stu2(stu2->isa)的地址是一样的
打印stu->age和stu2->age地址是一样的
我们再使用viewMemory工具来看

我们也可以看出:一个isa所占8个字节,2个int各占4和字节

结论:
Student对象
@interface Student : NSObject{
@public
int age;
int number;
}
@end
本质:
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int age;
int number;
};
其中NSObject_IMPL为:
struct NSObject_IMPL {
Class isa;
};
思考题:
以下person和student实例对象各占多少字节?
#import <Foundation/Foundation.h>
@interface Person : NSObject{
@public
int age;
}
@end
@implementation Person
@end
@interface Student : Person{
@public
int score;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc]init];
stu->age = 1;
stu->score = 2;
NSLog(@"%p,%p",&(stu->age),&(stu->score));
NSLog(@"End");
}
return 0;
}
通过转化为C\C++代码:
struct Student_IMPL {
struct Person_IMPL Person_IVARS;//16个字节
int score;//4个字节
};//16个字节(因为Person_IMPL正好空了4个字节出来)
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;//8个字节
int age;//4个字节
};//16个字节(内存对齐:结构体的大小必须是最大成员大小的倍数)
struct NSObject_IMPL {
Class isa;//8个字节
};
//通过viewMemory我们也可以看出来个数为16

下面我们改变下 我们修改score类型为double来看看
@interface Person : NSObject{
@public
int age;
}
@end
@implementation Person
@end
@interface Student : Person{
@public
double score;
}
@end
@implementation Student
@end
转化为C\C++代码
struct Student_IMPL {
struct Person_IMPL Person_IVARS;//16字节
double score;//8个字节
};//32个字节(内存对齐:结构体的大小必须是最大成员大小的倍数)
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;//8个字节
int age;//4个字节
};//16个字节
struct NSObject_IMPL {
Class isa;//8个字节
};
Student *stu = [[Student alloc]init];
stu->age = 1;
stu->score = 2;
NSLog(@"%p,%p",&(stu->age),&(stu->score));
NSLog(@"%zd", malloc_size((__bridge const void *)stu));//32
NSLog(@"End");
使用viewMemory来验证

内存图图见下:

我们再来个栗子:(来看看内存的具体分布)
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface Person : NSObject{
int _age;
int _height;
int _no;
}
@end
@implementation Person
@end
struct Person_IMPL{
Class isa;
int _age;
int _height;
int _no;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
NSLog(@"%zd", sizeof(struct Person_IMPL)); // 24
NSLog(@"%zd", class_getInstanceSize([Person class]));//24
NSLog(@"%zd", malloc_size((__bridge const void *)(p)));//32
}
return 0;
}
使用终端转为C\C++:
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;//8个字节
int _age;//4个字节
int _height;//4个字节
int _no;//4个字节
};//24字节(结构体内存对齐)
struct NSObject_IMPL {
Class isa;
};
分析:我们在 Person *p = [[Person alloc] init];处打一个断点,然后单步走和断点的方式
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;
}
发现此时的返回的size的大小就是24个字节
但为啥 是32个字节呢??(猜测 估计是malloc方法里面做了处理)
通过分析malloc源码
或者libmalloc源码编译,自己可以去试着打个断点去分析下
在nano_zone_common.h里面
NANO_MAX_SIZE: Buckets sized {16, 32, 48, ..., 256}
真正在底层ios(苹果操作系统)分配的时候还会对齐一下(以方便自己访问最快),有空间桶这个概念(16的倍数给的)
我们在xcode里面在来验证下:
#import <Foundation/Foundation.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
char *m;
m = (char *)(malloc(24)); //动态分配24个字节
NSLog(@"所占大小%lu",malloc_size(m)); //32
}
return 0;
}
发现打印出来的:实际上系统分配了32个字节
补充:
class_getInstanceSize:创建一个实例对象,至少需要多少内存
malloc_size:创建一个实例对象,系统实际上分配了多少内存
sizeof:是一个运算符:在程序编译的时候就知道值了 如(sizeof (p)) Persion *p = [Persion alloc]init];
而:class_getInstanceSize 是在运行时才知道的
友情链接:
网友评论