标签:
今天我们简单的对内存管理有个简单的认识,首先我们要提出的疑问就是为什么要使用内存管理,举个例子吧,我们在使用iPhone手机应用的时候,经常会出现以下闪退的应用,或者下载下来就Crash的,90%以上的原因都是因为内存的管理问题,仅凭这一点就值得我们重视内存管理.但是在一个拥有数十个甚至上百个的工程里,查找内存问题是极其困难的, 我们学习了解内存,以及内存会常见的问题,就能够帮助我们减少出错几率.
内存问题体现在两个方面: 内存溢出 , 野指针异常
内存溢出:iOS给每个应用程序提供了一定的内存,用于程序的运行. iPhone 3GS内存30M左右,iPhone 5S 内存80M左右,一旦超出内存上限,程序就会Crash. 程序中最占内存的就是图片,音频,视频等资源文件.3.5寸非Retina屏幕(320*480)放一张全屏图片,占用字节数320*480*4(一个像素占4个字节,存放RGBA),即:600k Bytes. iPhone 3GS同时读取60张图片就会crash. 4寸屏幕(320*568),实际像素640*1136,程序存放一张全屏图片,占用字节数640*1136*4,即2.77M Bytes.iPhone 5S同时读取40张图片就会crash.
野指针异常:对象内存空间已经被系统回收,仍然使用指针操作这块内存,野指针异常是程序crash的主要原因,代码量越来越大的程序,就会越来越难找出野指针的位置. 把内存管理学习好,能够帮助我们提升程序的性能,也可以大大减少调试Bug的时间.
内存管理的方式:包含垃圾回收(gc),MRC(Manual Reference Count),ARC(Auto Reference Count).
1. 垃圾回收:程序员只需要开辟内存空间,不需要用代码释放,系统来判断不被使用的空间,然后回收这些内存空间,然后进行内存分配. 整个回收的过程不需要写任何代码,由系统自动完成垃圾回收.
2. MRC: 人工引用计数:内存的开辟和释放都由代码进行控制,相对垃圾回收来说,对内存的控制的更加灵活,可以在需要释放的时候及时释放,对程序员的要求较高,程序员要熟悉内存管理的机制,要遵守一个内存的黄金法则, 谁开辟了空间,就要谁释放,也就是只要有alloc init,那么就要对开辟的空间进行释放.
3.ARC:自动引用计数:iOS 5.0的编译器特性,它允许用户只开辟空间,不用去释放空间,它和垃圾回收机制不一样,它的本质还是MRC,只是编译器帮程序员默认加了释放的代码,后续在iOS开发阶段的时候,可以更深刻的了解这一点.
iOS支持两种内存管理方式:ARC和MRC, MRC的内存管理机制是引用计数,ARC是基于MRC的. 关于引用计数,我们在一一做介绍. 这些都是我们以后会经常遇到的,希望大家不要觉得烦闷,认为这东西太概念化,能不能处理好内存管理对你的应用有直接的关系. 因为代码都是大同小异,但是优化内存是一个细心出慢活的任务.
引用计数:在C语言中,我们都使用过malloc和free,进行堆内存的创建和释放,堆内存只有正在使用和销毁两种状态, 在实际开发中,可能会遇到,两个以上的指针使用同一块内存,C语言无法记录内存使用者的个数. 但OC的引用计数机制管理内存,当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减,当引用计数到零时,该对象就讲释放占有的资源, 好好理解一下这段话.
影响引用计数的方法:+alloc -retain -copy -release -autorelease
+allco:开辟内存空间,如果还不知道alloc是干嘛的,那就赶紧回家放羊去吧. 给创建的对象开辟内存空间,这时候他的引用计数变为1,这是从0到1的过程.
-retain:引用计数加1,如果内存空间之前引用计数为1,那么retain之后就会变为2,如果引用计数是5,retain之后就会变成6. 字面意思很容易理解的,多多操作一下,就会更加深刻.
-copy:把某一内存区域的内容拷贝一份,拷贝到新的内存空间里去,被拷贝的区域的引用计数不变,新的内存区域的引用计数为1. 不要和retain搞混了,一般人的痔疮,对copy和retain会有些混淆. copy和retain不同,一个对象想要copy,生成自己的副本,需要实现NSCopying协议,定义copy细节(如何copy),如果类没有接收NSCopying协议而给对象发送copy消息,会引起crash. 实现方法如下:
Person.h文件
@interface Person:NSObject <NSCopying>
@property (nonatomic,copy)NSString *name;
@property (nonatomic,assign)NSInteger age;
@end
Person.m文件
@implementation Person
- (id)copyWithZone:(NSZone *)zone{
Person *p = [[Person allcoWithZone:zone]init];
p.age = self.age;
p.name = self.name;
return p;
}
main.m文件
Person *p = [[Person alloc]init];
p.name = @"Jack";
p.age = 20;
Person *p2 = [p copy];
p2是p的副本,p2.name与p.name一样. p2.age与p.age一样.
-release:引用计数减1,如果内存空间之前引用计数为4,release之后变为3,如果引用计数为1,release之后变为0,内存就会被系统回收.
-deallco:是集成父类的方法,当对象引用计数为0的时候,由对象自动调用.我们可以通过代码来验证引用计数是否降为0了. 代码如下:
- (void)dealloc
{
NSLog(@"%@被销毁了",self);
[super dealloc];
}
-autorelease:未来的某一时刻引用计数减1. 如果内存之前引用计数为4,autorelease之后仍然为4,未来的某个时刻会变为3.这么说有点太含糊了,不容易理解,我们在详细的解释一下.
要详细了解autorelease,首先我们要先了解autoreleasepool(自动释放池), autorelease对象的释放是由autoreleasepool来控制的. 向一个对象发送autorelease消息,这个对象何时释放,取决于autoreleasepool. 我们用代码来解释一下autoreleasepool的使用:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
Person *p = [[Person alloc]init];//retainCount为1
[p retain];//retainCount为2
[p autorelease];//retainCount为2 暂时还没有释放,而是在未来的某一时刻释放
[pool release];//此时 autolease的对象引用计数 -1
NSLog(@"%d",[p retainCount]);//打印结果为1
解释:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];和[pool release];就像一对括号,[xxx autorelease];必须写在两者之间.
[xxx autorelease];出现在了两者之间,pool就会把接受autorelease的对象给保存起来(以栈的方式,把对象压入栈) 当[pool release];的时候,pool会向之前保存的对象一一发送release消息(对象出栈,越晚autorelease的对象,越早接收release消息).
在iOS之后,不再推荐使用NSAutoreleasePool类,而是使用@autoreleasepool{}替代,之前写在NSAutoreleasePool *pool = [[NSautoreleasePool alloc]init];和[pool release];之间的代码,需要写在@autoreleasepool{}大括号里. 出了大括号,自动释放池才向各个对象发送release消息.
内存管理的原则: 引用计数的增加和减少相等,当引用计数将为0之后,不应该在使用这块内存空间.
内存管理黄金法则:凡是使用了alloc,retain或者copy让内存的引用计数增加了,就需要使用release或者autorelease让内存的引用计数减少,在一段代码,增加和减少的次数要相等.
这些文字性的东西,看起来会很烦操, 但希望大家可以好好理解一下,因为内存管理是非常重要的,我是重新返工来理解这些概念性的东西. 但我不希望你们和我一样忽视这最不该忽视的.
标签:
原文地址:http://www.cnblogs.com/why2wm/p/4434538.html