复习C语言中的内存:
int *p = malloc(8); —>堆内存
free(p);
free(p); —>过度释放
- 内存溢出: 内存不释放
- 野指针: free之后还可以用p找到那块内存, 因为是标记删除; 但内存中内容不能保证还是原来的, 因为所有权已还给系统
野指针异常是程序crash主要原因
将ARC改为MRC:
- ARC Automatic Reference Counting 自动引用计数
- MRC Manual Reference Counting 手动引用计数
- 关闭ARC就是MRC
- ARC是基于MRC的
内存管理:
1. - retainCount: 打印当前对象的引用计数, 结果只能作为一个参考
2. + alloc: 开辟内存空间,让被开辟的内存空间引用计数变为1, 这是由0到1的过程
Person *person1 = [[Person alloc] init];
此时 person1 引用计数由0变为1
NSLog(@"%lu", [person1 retainCount]);
打印结果: 1
3. - retain: 引用计数+1
[person1 retain];
[person1 retain];
person1 引用计数两次 +1
NSLog(@"%lu", [person1 retainCount]);
打印结果: 3
4. - release: 引用计数-1, 而不是释放
[person1 release];
person1引用计数 -1
NSLog(@"%lu", [person1 retainCount]);
打印结果: 2
[person1 release];
NSLog(@"%lu", [person1 retainCount]);
打印结果: 1
[person1 release];
NSLog(@"%lu", [person1 retainCount]);
打印结果: 1(*为何是1而不是0?)
[person1 release];
过多的释放,会导致crash
内存的所有权:
所有权是管理内存的根本性的问题, 一个指针能不能对内存进行release, 就看有没有所有权.
只有开辟空间的对象才拥有内存的所有权.
5. - autorelease: 出自动释放池 ( @autoreleasepool {}) 时-1
Person *person2 = [[Person alloc] init];
@autoreleasepool {
[person2 retain];
[person2 retain];
[person2 release];
[person2 autorelease];
[person2 autorelease];
// [person2 autorelease]; —>同样会造成过度释放
NSLog(@"%lu", [person2 retainCount]);
打印结果: 2
} —> 在出释放池,运行释放池后第一个语句之前会执行dealloc
NSLog(@"%lu", [person2 retainCount]);
打印结果: 1
|
6. - dealloc: 释放内存, 当对象的引用计数变为0的时候, 系统会自动调用dealloc方法, 可以重写父类的dealloc方法, 重写时不用声明, 不能手动调用.
在类中重写dealloc方法:
- (void)dealloc{
[_name release]; —> 将由成员变量指向的内存空间的引用计数-1. 基本数据类型的成员变量不需要release, 因为在栈区, 由系统自动管理
[_sex release];
[super dealloc];
}
|
调用父类的dealloc方法,释放公共部分的属性和成员变量, [super dealloc]; 作为方法的最后一句.
7. - copy: 会开辟一段内存空间, 和原来大小一致, 存储内容一致, 会保持原来的引用计数不变, 新的内存引用计数变为1. 遵循NSCopying协议才能使用copy.
想要使自己创建的Person类使用copy方法:
1. 签订NSCopying协议: @interface Person : NSObject<NSCopying>
2. 在.h文件中实现NSCopying协议中声明的方法: - (id)copyWithZone:(NSZone *)zone;
- (id)copyWithZone:(NSZone *)zone{
NSZone, 内存池, 里面的内存都是没用过的
Person *p = [[Person allocWithZone:zone] init];
使用拷贝区zone, 创建一个新的对象, 相当于开辟空间, 并初始化
p.name = self.name;
p.sex = self.sex;
把对象自己的属性内容给新的对象拷贝一份
return p;
}
|
3. 使用copy方法: Person *person8 = [person7 copy];
*关于release方法的实现:
系统不让重写release方法, 此处仅为演示:
//- (void)release{
//
// if (self.retainCount == 1) {
self.retainCount—; —>当引用计数为1的时候调用release,目的为将内存还给系统,所以没有必要再-1了,直接释放内存即可
// [self dealloc];
// }else{ // self.retainCount--; // }
//}
|
内存管理的原则:
- 当看到alloc, retain, copy时, 要写release, 要保证引用计数的加减平衡
- 如果但不到, 则不管
说明:
引用计数的变化针对的是内存空间, 而不是指针变量.
Person *person001 = [[Person alloc] init];
NSLog(@"%lu", [person001 retainCount]); —> 打印结果: 1
[person001 retain];
[person001 retain];
NSLog(@"%lu", [person001 retainCount]); —> 打印结果: 3
Person *person002 = person001;
NSLog(@"%lu", [person002 retainCount]); —> 打印结果: 3
此时person002 与 person001 指的是同一块内存空间
[person002 release];
NSLog(@"%lu", [person001 retainCount]); —> 打印结果: 2
当对person2进行release操作时, 使用person001调用retainCount打印出的结果同样会-1
|
数组的内存管理:
Person *person3 = [[Person alloc] init];
NSMutableArray *arr = [NSMutableArray array];
[arr addObject:person3];
NSLog(@"%lu, %lu", [arr retainCount], [person3 retainCount]);
打印结果: 1, 2
[arr removeObject:person3];
NSLog(@"%lu", [person3 retainCount]);
打印结果: 1
|
被加入到数组中的元素会被retain一次.
当元素被移除的时候,元素会被release一次.