OC-内存管理

常用概念

内存泄露

该释放的对象没有释放

iOS程序的内存布局

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

Tagged Pointer

从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumberNSDateNSString等小对象的存储

在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber`对象的地址值

使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中。当指针不够存储数据时,才会使用动态分配内存的方式来存储数据

objc_msgSend能识别Tagged Pointer,比如NSNumberintValue方法,直接从指针提取数据,节省了以前的调用开销

如何判断一个指针是否为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 - 手动内存管理

当调用allocnewcopymutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它

复杂数据类型(retain,strong)的setter和getter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)seDog:(Dog *)dog {
if (_dog != dog) {
[_dog release];
_dog = [dog retain];
}
}
- (Dog *)dog {
return _dog;
}
- (void)dealloc {
[_dog release];
_dog = nil;
// 或者
// self.dog = nil;
[super dealloc];
}

基础数据类型(assign)的setter和getter

1
2
3
4
5
6
- (void)seAge:(int)age {
_age = age;
}
- (int)age {
return _age;
}

涉及不到内存管理操作

遵循NSCoping协议类型(copy)的setter和getter

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)seName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name copy];
}
}
- (NSString *)name {
return _name;
}
- (void)dealloc {
[_name release];
[super dealloc];
}

遍历构造器
譬如[NSArray array]这种遍历构造器生成的对象,内部做了autorelease的处理,外部不需要再进行release操作

集合

copy

在OC中,只有NSDataNSStringNSSetNSArrayNSDictionary这些类支持copy操作。如果想对其他类实现copy操作,如下代码去实现
遵循NSCoping协议

1
<NSCoping>

实现协议方法

1
2
3
4
5
6
- (id)copyWithZone:(NSZone *)zone {
Person *person = [[Person alloc] init];
person.age = self.age;
person.name = self.name;
return person;
}