主要内容:内存管理初级
一、内存管理介绍
为什么要使用内存管理?
iOS程序出现Crash(闪退)%90以上都是内存的问题
好处:在后期项目成十上百的类中查找内存问题是极其困难的,学习内存的常见问题,能帮助我们很好的减少出错几率
内存问题体现在两个方面:内存溢出、野指针异常
iOS给每个应用程序提供了一定内存,用于程序运行(简称运行内存),超出了运行内存,程序就会Crash(闪退)
野指针异常:对象内存空间已经被系统收回,仍然使用指针操作这块内存
注意:野指针异常是程序Crash的主要原因
内存管理的方式:
1、垃圾回收(gc):程序员只需要开辟内存空间,不需要用代码显示释放,系统会自动的收回不在使用的空间,以便再次使用,整个回收的过程中不需要写任何代码,系统自动的完成垃圾回收
2、MRC(Manual Reference Count) 手动管理内存:人工引用计数,内存的申请、释放都由程序员以代码进行控制,控制内存的方式更加灵活,可以在需要释放的时候及时释放,对程序员的要求较高,需要程序员很熟悉内存管理机制
3、ARC(Auto Reference Count) 自动管理内存:自动引用计数,iOS5.0以后允许只申请空间,不用释放空间,系统会默认的加上释放的代码(本质还是MRC),不是垃圾回收
注意:
1、内存管理的方式是:MRC、ARC
2、内存管理的机制是:引用计数
3、ARC是基于MRC的
二、内存管理机制(引用计数)
C语言中使用alloc和free进行堆内存的申请和释放,堆内存只有正在使用和销毁两种状态,而实际开发中,可能会遇到两个以上的指针指向同一个内存,C语言不能记录内存使用者的个数
OC中采用 “ 引用计数 ” 的方法管理内存机制,当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数器就递减。当引用计数器为0时,该对象将释放占有的资源(即该内存将会被释放)
影响引用计数器的方法
1、alloc:开辟内存空间,让被开辟的内存空间的引用计数变为 1,是从 0 到 1 的过程
2、retain:引用计数加 1 ,示例:如果引用计数之前为 3,retain之后变为 4
3、copy:把某一内存区域内的内容拷贝一份,拷贝到新的内存空间里,被拷贝区域的引用计数不变,新的内存区域的引用计数为 1
4、release:引用计数减 1 ,示例:如果引用计数之前为 3,release之后变为 2
5、autorelease:未来的某一时刻引用计数减 1,示例:如果引用计数之前为 3,autorelease之后还是 3,未来某一时刻会减 1
注意:使用autoreleasepool控制autorelease的释放,向一个对象发送autorelease消息,该对象何时释放,取决于autoreleasepool
dealloc方法:是继承自父亲的方法,当对象引用计数为 0 时,由对象自动调用
可以在dealloc中打印一句话来验证引用计数是否为 0
- (void) dealloc
{
NSLog(@" %@ 被销毁了 " , self ) ;
[super dealloc];
}
autoreleasepool的使用:控制autorelease的释放
iOS5.0之前使用NSAutoreleasePool类
示例:
/ / 自动释放池
NSAutoreleasePool *autoreleasepool = [ [ NSAutoreleasepool alloc ] init ] ;
Person *person = [ [ Person alloc ] init ] ;
[ person retain ] ;
NSLog(@"%@" , person.retainCount) ; / / 打印引用计数
[ person autorelease ] ;
/ / 销毁自动释放池
[ autoreleasepool release ] ;
iOS5.0之后不再推荐使用NSAutoreleasePool类,使用@autoreleasepool { } ;代替
内存管理的原则
引用计数的递增和递减相等,当引用计数为 0 之后,不应该在使用这块内存空间
注意:凡是使用alloc、retain或者copy让内存的引用计数增加了,就要用release或者autorelease使内存的引用计数减少,在一段代码内,增加和减少的次数要相等
三、copy
copy和retain不同,一个对象想要拷贝生成自己的副本,需要实现(NSCopying)协议,定义copy的细节,如果类没有接受 NSCopying协议而向对象发送copy消息,会导致Crash
copy方法的实现
示例:
Person.h 文件
@interface Person : NSObject <NSCopying>
@property (nonamotic, copy) NSString *name;
@property (nonamotic, assign) NSIteger age;
@end
Person.m文件
@implementation Person
- (id)copyWithZone:(NSZone *)zone
{
Person *person = [ [ Person alloc ] init ] ;
person.name = self.name ;
person.age = self.age ;
return person ;
}
main.m文件
Person *p = [ [ Person alloc ] init ] ;
p.name = @" 张三 " ;
p.age = 21 ;
Person *p2 = [ p copy ] ;
p2是p的副本,p.name和p2.name、p.age和p2.age是相等的
注意:必须是遵守了NSCopying协议的对象才能接受copy对象