iOS程序的内存布局
- 栈区(stack):
- 存放函数的参数值、局部变量的值等,
- 由编译器自动分配释放,通常在函数执行结束后就释放了,其操作方式类似数据结构中的栈。
- 栈内存分配运算内置于处理器的指令集,效率很高。
- 分配的内存容量有限,比如iOS中栈区的大小是512k。
- 堆区(heap)
- C 语言使用 malloc、calloc、realloc 函数分配的空间,需要使用 free 函数释放
- 由程序员分配释放,若程序员不释放,会出现内存泄漏
- 分配方式类似于链表。
- 堆区的大小由系统决定,包括:系统内存/磁盘交换空间
栈区的特点
- 栈区的地址是连续的
- 栈区地址按照分配的顺序,由大到小顺序排列
- 访问速度快、栈区的内存由系统管理、后进先出/先进后出
堆区的特点
堆区的大小由系统决定,包括:系统内存/磁盘交换空间
- 所有程序共享、存储大数据、程序员管理
- 堆区的地址是不连续的
速度没有栈区快
如果只有一个方法使用,将 静态变量 定义在方法内部
- 如果有多个方法使用,将 静态变量 定义在 .m 中
-
常量
在
数据段
为常量分配空间- const 关键字保证其后修饰的常量的值不允许被修改
- 在程序被加载到内存时,就会为常量分配空间并且设置初始值
- 如果没有指定初始值,会使用 0 作为初始值
- 常量名应该尽量的长以避免出现重名
//在 .m 中定义常量并且设置初始值
const NSInteger cNum = 99;
//在 .h 中使用 extern 关键字声明常量在其他位置定义并且已经赋值,外部可以直接使用
extern const NSInteger cNum;
验证
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
如何判断一个指针是否为Tagged Pointer?iOS平台,最高有效位是1(第64bit)
- Mac平台,最低有效位是1
OC对象的内存管理
- 在iOS中,使用引用计数来管理OC对象的内存
- 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
- 调用
retain
会让OC对象的引用计数+1,调用release
会让OC对象的引用计数-1 - 内存管理的经验总结
- 当调用
alloc
、new
、copy
、mutableCopy
方法返回了一个对象,在不需要这个对象时,要调用release
或者autorelease
来释放它 - 想拥有某个对象,就让它的引用计数
+1
;不想再拥有某个对象,就让它的引用计数-1
- 当调用
- 可以通过以下私有函数来查看自动释放池的情况
extern void _objc_autoreleasePoolPrint(void);
copy和mutableCopy
- 可变对象的
copy
和mutableCopy
都是深拷贝 - 不可变对象的
copy
是浅拷贝,mutableCopy
是深拷贝 copy
方法返回的都是不可变对象 | | copy | mutableCopy | | —- | —- | —- | | NSString | NSString
浅拷贝 | NSMutableString
深拷贝 | | NSMutableString | NSString
深拷贝 | NSMutableString
深拷贝 | | NSArray | NSArray
浅拷贝 | NSMutableArray
深拷贝 | | NSMutableArray | NSArray
深拷贝 | NSMutableArray
深拷贝 | | NSDictionary | NSDictionary
浅拷贝 | NSMutableDictionary
深拷贝 | | NSMutableDictionary | NSDictionary
深拷贝 | NSMutableDictionary
深拷贝 |
引用计数的存储
在64bit
中,引用计数可以直接存储在优化过的isa指针中,也可能存储在SideTable
类中