内存布局

  • 代码段:编译之后的代码
  • 数据段
    • 字符串常量:比如NSString *str = @”123”
    • 已初始化数据:已初始化的全局变量、静态变量等
    • 未初始化数据:未初始化的全局变量、静态变量等
  • 栈:函数调用开销,比如局部变量。分配的内存空间地址越来越小
  • 堆:通过alloc、malloc、calloc等动态分配的空间,分配的内存空间地址越来越大

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

int a = 10;
int b;

int main(int argc, char * argv[]) {
@autoreleasepool {
static int c = 20;

static int d;

int e;
int f = 20;

NSString *str = @"123";

NSObject *obj = [[NSObject alloc] init];

NSLog(@"\n&a=%p\n&b=%p\n&c=%p\n&d=%p\n&e=%p\n&f=%p\nstr=%p\nobj=%p\n",
&a, &b, &c, &d, &e, &f, str, obj);

return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

/*
字符串常量
str=0x10dfa0068

已初始化的全局变量、静态变量
&a =0x10dfa0db8
&c =0x10dfa0dbc

未初始化的全局变量、静态变量
&d =0x10dfa0e80
&b =0x10dfa0e84


obj=0x608000012210


&f =0x7ffee1c60fe0
&e =0x7ffee1c60fe4
*/

Tagged Pointer

  • 从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储
  • 在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
  • 使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
  • 当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
  • objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销
  • 如何判断一个指针是否为Tagged Pointer
    • iOS平台,最高有效位是1(第64bit)
    • Mac平台,最低有效位是1

objc-internal.h

1
2
3
4
5
6
7
8
9
10
11
12
13
#if TARGET_OS_OSX && __x86_64__
// 64-bit Mac - tag bit is LSB
# define OBJC_MSB_TAGGED_POINTERS 0
#else
// Everything else - tag bit is MSB
# define OBJC_MSB_TAGGED_POINTERS 1
#endif

#if OBJC_MSB_TAGGED_POINTERS
# define _OBJC_TAG_MASK (1UL<<63)
#else
# define _OBJC_TAG_MASK 1UL
#endif

思考以下2段代码能发生什么事?有什么区别?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//此段代码会崩溃,因为[NSString stringWithFormat:@"abcdefghijk"];以对象方式执行
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
// 加锁
self.name = [NSString stringWithFormat:@"abcdefghijk"];
// 解锁
});
}

// 下面代码没有问题,[NSString stringWithFormat:@"abc"];是Tagged Pointer方式执行
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abc"];
});
}

参考