OC-内存管理
常用概念
内存泄露
该释放的对象没有释放
iOS程序的内存布局

- 代码段:编译之后的代码
- 数据段
- 字符串常量:比如NSString *str = @”123”
- 已初始化数据:已初始化的全局变量、静态变量等
- 未初始化数据:未初始化的全局变量、静态变量等
- 栈:函数调用开销,比如局部变量。分配的内存空间地址越来越小
- 堆:通过alloc、malloc、calloc等动态分配的空间,分配的内存空间地址越来越大
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);WWDC2020:最低有效位是1
Mac平台,最低有效位是1
OC对象的内存管理
在iOS中,使用引用计数来管理OC对象的内存.
一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
调用
retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1想
拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1
@property
旧:生成一个成员变量,以及setter和getter的声明,搭配@synthesize使用
新:生成一个下划线开头成员变量,以及setter和getter的声明及实现
@synthesize
自动生成成员变量和属性的setter,getter实现
1 | @synthesize dog = _dog; |
@autorelease
系统会在恰当的时候进行释放
可以通过以下私有函数来查看自动释放池的情况
1 | extern void _objc_autoreleasePoolPrint(void); |
拷贝
产生一个副本对象,跟源对象不影响. 修改了源对象,不会影响副本对象;修改了副本对象,不会影响源对象。
iOS提供两种拷贝
- copy 不可变拷贝,产生不可变副本
- mutableCopy,可变拷贝,产生可变副本
深拷贝和浅拷贝
- 浅拷贝:指针拷贝
- 没有产生新对象
- 引用计数+1
- 深拷贝:内容拷贝
- 产生新的对象。
- 新的对象引用计数为1
| copy | mutableCopy | |
|---|---|---|
| NSString | NSString浅拷贝 | NSMutableString深拷贝 |
| NSMutableString | NSString深拷贝 | NSMutableString深拷贝 |
| NSArray | NSArray浅拷贝 | NSMutableArray |
| NSMutableArray | NSArray深拷贝 | NSMutableArray深拷贝 |
| NSDictionary | NSDictionary浅拷贝 | NSMutableDictionary深拷贝 |
| NSMutableDictionary | NSDictionary深拷贝 | NSMutableDictionary深拷贝 |
copy 并不代表浅拷贝。如果对一个可变对象做copy操作,那么其就是深拷贝。
MRC - 手动内存管理
当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
复杂数据类型(retain,strong)的setter和getter
1 | - (void)seDog:(Dog *)dog { |
基础数据类型(assign)的setter和getter
1 | - (void)seAge:(int)age { |
涉及不到内存管理操作
遵循NSCoping协议类型(copy)的setter和getter
1 | - (void)seName:(NSString *)name { |
遍历构造器
譬如[NSArray array]这种遍历构造器生成的对象,内部做了autorelease的处理,外部不需要再进行release操作
集合
copy
在OC中,只有NSData,NSString,NSSet,NSArray,NSDictionary这些类支持copy操作。如果想对其他类实现copy操作,如下代码去实现
遵循NSCoping协议
1 | <NSCoping> |
实现协议方法
1 | - (id)copyWithZone:(NSZone *)zone { |