1. 为什么要使用内存管理
iOS应用程序出现crash(闪退), 90%以上的原因是出现了内存问题!
内存问题主要出现在内存溢出和野指针异常!
内存溢出
iOS给每个应用提供了一定的内存, 用于程序的运行, 一旦超过内存的上限,就会Crash,
野指针异常
对象内存空间已经被系统回收, 然而去还用指针指使用了这块内存!
2. 内存管理的分类
垃圾回收机制(gc);
垃圾回收机制: 程序员只需要开辟内存空间, 不需要用代码去释放! 系统自己来判断那些空间不再被使用, 并且回收这些内存空间以便再次分配, 整个回收的过程不需要写任何代码, 有系统自动完成垃圾回收, java开发中一直使用的就是垃圾回收机制!
MRC(Manual Reference Count) ; 人工引用计数;
人工引用计数: 内存的开辟和释放都是由程序代码进行控制, 相对于垃圾回收机制来说,对内存的控制更加灵活, 可以在需要释放的饿时候及时释放, 对程序员的要求较高, 程序员要熟悉内存的管理的机制!
ARM (Auto Reference Count);
自动引用计数:iOS 5.0的编译器特性, 它允许用户开辟空间, 不用去释放空间, 它不是垃圾回收! 她的本质还是MRC, 只是编译器帮程序员默认加了释放的代码,
iOS 支持MRC和ARC
3. 引用计数
C语言中,使用malloc 和 free 进行堆内存, 进行堆内存的创建和释放, 对内存只有正在使用和销毁两种状态!
实际开发中, 可能遇到两个以上的指针使用同一块内存, C语言无法记录内存使用个数!
OC采用引用计数管理内存, 当一个新的引用指向对象的时候, 引用计数器就递增, 当去掉一个引用时, 引用计数就递减, 当引用计数, 到零的时候, 该对象就是占有的资源!
OC引用技术的方法
-
alloc 引用计数+1;
Person *p = [[Person alloc] init];
// 显示引用计数
NSLog(@"%ld", [p retainCount]);
// 引用计数 1 -
retain 引用计数+1
[p retain];
NSLog(@"%lu", [p retainCount]); // 引用计数 2; -
release 引用计数 -1
[p release];
NSLog(@"%lu", [p retainCount]); 引用计数 1;
但是, 当p再次release一次的时候,
[p release] // -1 此时p的引用计数为0, 因而对象应该是已被释放的!
NSLog(@"%lu", [p retainCount]); 打印却为1;
打出的引用计数虽然为1, 但他的实际为零, 对象已经被释放的!
- autorelease autorelease引用计数不是立即 -1, 在未来的某一时刻(出了 autoreleasepool(自动释放池)) 之后, 引用计数再 -1**
通过autoreleasepool控制autorelease对象的释放!
向一个对象发送autorelease消息, 这个对象何时释放, 取决于autoreleasepool(自动释放池子)!
Person *p2 = [[Person alloc] init];
@autoreleasepool {
[p2 retain]; 引用计数+1 为2;
[p2 autorelease]; 此时引用计数为2;
NSLog(@"%lu", [p2 retainCount]);
}
NSLog(@"%lu", [p2 retainCount]);
// 出了释放池, 计数 -1
- copy 引用几计数 +1
对象想使用copy, 必须签订copy协议, 实现copy协议
person类签订copy协议, 实现copy方法
如下.h 和 .m 文件
Person .h 文件
// 签订copy协议
#import
@interface Person : NSObject <NSCopying>
@property(nonatomic, assign)NSString *name;
@property(nonatomic, assign)NSString *age
@end
Person .m文件 // 必须重写delloc方法, 因为
#import "Person.h"
@implementation Person
// 重写alloc方法
- (void)dealloc
{
NSLog(@"******"); // 定义一个, 用于标记
[super dealloc];
}
// 实现copy协议方法
- (id)copyWithZone:(NSZone *)zone
{
Person *p = [[Person allocWithZone:zone] init];
p.name = self.name;
p.age = self.age;
return p;
}
调用!
Person *p3 = [[Person alloc] init];
p3.name = @"tom";
p3.age = @"20";
// 使用copy创建
Person *p4 = [p3 copy];
NSLog(@"--- %d ---", LINE);
NSLog(@"%@, %@", p4.name, p3.age);
NSLog(@"%lu", [p3 retainCount]);
// p4 的引用计数是多少?
NSLog(@"%lu", [p4 retainCount]); // 用copy 计数 会+1;
- dealloc
- dealloc是继承自父类的方法,当对象引用计数为0的时候,由对象自动调用。用于释放内存区域;
- 必须重写delloc方法, 在.m文件中
- 加入数组的时候, 对象retain一次, 引用计数+1;
Person *p5 = [[Person alloc] init];
[arr addObject:p5]; - 对象移除数组的时候, 对象release一次, 引用计数 -1;
[arr removeObject:p5];
添加和删除, 系统内部都帮你实现了!
内存管理原则;
- 引用计数的增加和减少相等,当引用计数降为0之后,不应该再使 用这块内存空间;
- 凡是使用了alloc、retain或者copy让内存的引⽤用计数增加了,就需要使用release或者autorelease让内存的引用计数减少。在一段代码内,增加和减少的次数要相等。
网友评论