size_t class_getInstanceSize(Class cls)
说明:该函数的作用是获取类的实例所占用内存的大小(单位是字节)
1. 以 NSObject
类为例,代码示例如下:
size_t size = class_getInstanceSize([NSObject class]);
NSLog(@"%zu", size);
在模拟器iPhone 4s和iPhone 5上运行打印结果是4,在iPhone 5s及之后的机型运行打印结果是8。
分析:Objective-C中,每个对象内部都有一个
isa
指针,指向该对象的类;一个类从另一角度来说也是一个对象,类也有一个isa
指针,指向该类的元类。
因此上述代码打印出的结果,就是NSObject
实例中isa
指针所占用的字节数。
至于不同机型打印结果不同,是因为目标系统位数的不同:iPhone 5s开始使用了64位的处理器及系统;和使用Xcode的电脑系统位数无关(iOS开发)。一个指针所占用的字节数在32位系统上为4字节,在64位系统上为8字节。其他基本数据类型在32位和64位系统中所占用的字节数也有所差异:
32位系统下:
char a; // 1
short b; // 2
int c; // 4
long d; // 4
float e; // 4
long long f; // 8
double g; // 8
int *h; // 4
64位系统下:
char a; // 1
short b; // 2
int c; // 4
long d; // 8
float e; // 4
long long f; // 8
double g; // 8
int *h; // 8
可见32位和64位系统中只有 long
和 指针
类型所占用的字节数不同。
2. 以其他类为例(以下均在64位的目标系统上进行测试)
(1). 自定义类 Person
类及代码示例如下
// Person.h
@interface Person : NSObject
/** name */
@property (nonatomic, copy) NSString *name;
/** age */
@property (nonatomic, assign) int age;
@end
// Person.m
@interface Person ()
{
NSString *childhoodName;
}
@end
// ViewController.m
size_t size = class_getInstanceSize([Person class]);
NSLog(@"%zu", size);
打印结果为32,这是内部的 isa
指针和两个 NSString
指针、一个 int
类型一共占用的字节数。刚才测试的 int
类型在64位系统下所占字节数为4,为什么现在和3个指针所占用的字节数加起来是32呢?这是因为『字节对齐』的原因(译文1,译文2,原文)。
(2). 自定义类 LittlePerson
类及代码示例如下
当一个类继承其他类时,会继承此类的所有属性和实例变量
// LittlePerson.h
@interface LittlePerson : Person
/** 学校 */
@property (nonatomic, copy) NSString *school;
/** 玩具 */
@property (nonatomic, strong) NSArray *toys;
/** 书 */
@property (nonatomic, assign) short bookCount;
@end
// LittlePerson.m
@interface LittlePerson ()
@end
// ViewController.m
size_t size = class_getInstanceSize([LittlePerson class]);
NSLog(@"%zu", size);
打印结果为56。
要注意的是 class_getInstanceSize
函数和 sizeof()
并不等同
size_t size = class_getInstanceSize([UIViewController class]);
NSLog(@"InstanceSize: %zu", size);
UIViewController *vc = [[UIViewController alloc] init];
NSLog(@"sizeof: %ld", sizeof(vc));
NSLog(@"sizeof: %ld", sizeof([UIViewController class]));
打印结果如下:
runtime[57907:5849922] InstanceSize: 696
runtime[57907:5849922] sizeof: 8
runtime[58021:5856762] sizeof: 8
3. 从字节对齐看苹果对内存的优化
在 Person
类中增加几个属性和实例变量,其中标号为数字1、2、3的是上述代码中已有的,变量名为单个字母(a--h)的是新增加的。代码示例如下:
// Person.h
@interface Person : NSObject
/** name */
@property (nonatomic, copy) NSString *name; // 1
/** age */
@property (nonatomic, assign) int age; // 2
/** other */
@property (nonatomic, copy) NSString *a;
@property (nonatomic, assign) int b;
@property (nonatomic, assign) char *c;
@property (nonatomic, assign) short d;
@end
// Person.m
@interface Person ()
{
NSString *childhoodName; // 3
short e;
char f[8];
NSString *g;
int h;
}
@end
// ViewController.m
size_t size = class_getInstanceSize([Person class]);
NSLog(@"InstanceSize: %zu", size);
我们先猜想一下打印结果。通过学习刚才链接中的字节对齐的知识,可以推测结果是96,我们先不公布打印结果,先按正常的字节对齐看看96是否正确。在 ViewController.m
中增加以下代码,一一对应 Person
实例中的属性和实例变量:
struct people {
char *isa;
char *name;
int age;
char *a;
int b;
char *c;
short d;
char *childhoodName;
short e;
char f[8];
char *g;
int h;
};
struct people p;
NSLog(@"sizeof:%ld", sizeof(p));
本次打印结果为96,对字节对齐的知识有所掌握
再回到刚才 Person
实例,打印结果如下:
runtime[58897:5911521] InstanceSize: 80
推测的并不正确。
如果手动对属性和成员变量『共同』重排进行优化,对应的 people
结构体如下:
struct people {
char *isa;
char *name;
char *a;
char *c;
char *childhoodName;
char *g;
char f[8];
int age;
int b;
int h;
short d;
short e;
};
struct people p;
NSLog(@"sizeof:%ld", sizeof(p));
此时打印结果为72,和 Person
实例所占用内存的大小仍不一样。
现在手动对属性和成员变量『分别』重排进行优化,对应的 people
结构体如下:
struct people {
char *isa;
char *name;
char *a;
char *c;
int age;
int b;
short d;
char *childhoodName;
char *g;
char f[8];
int h;
short e;
};
struct people p;
NSLog(@"sizeof:%ld", sizeof(p));
此时打印结果为80,和 Person
实例所占用内存的大小相同。可见:
苹果会自动对属性和成员变量进行分别重排从而优化内存
这一切不需要我们手动去优化,大大地节省了程序员的时间。
很早之前就听说苹果会变动一些代码的顺序进行优化,今天总算知道会出现在哪里了