runtime使用篇: class_getInstanceSize 和 苹果对内存的优化

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 实例所占用内存的大小相同。可见:
苹果会自动对属性和成员变量进行分别重排从而优化内存
这一切不需要我们手动去优化,大大地节省了程序员的时间。
很早之前就听说苹果会变动一些代码的顺序进行优化,今天总算知道会出现在哪里了

你可能感兴趣的:(runtime使用篇: class_getInstanceSize 和 苹果对内存的优化)