一、关于内存管理
应用程序内存管理是在程序运行时分配、使用以及在使用完成后释放的过程。一个写得好的程序使用尽可能少的内存。
虽然内存管理通常是在单个对象的级别上考虑的,但您的目标实际上是管理对象图。
两种内存管理的方式:
MRC:可以通过跟踪自己拥有的对象来显式管理内存。使用引用计数的模型来实现。
ARC:系统使用与MRC相同的引用计数系统,但它在编译时插入了适当的内存管理方法。(建议使用)
两个内存方面的常见问题:
释放或覆盖仍在使用的数据
这会导致内存损坏,通常会导致应用程序崩溃,或者更糟糕的是用户数据损坏。
不释放不再使用的数据会导致内存泄漏
内存泄漏是分配的内存不被释放的地方,即使它再也不会被使用。泄漏导致您的应用程序使用越来越多的内存,这反过来可能导致系统性能低下或您的应用程序被终止。
二、内存管理的基本规则
1、你拥有你创建的对象:
使用一个以“alloc”、“new”、“copy”或“mutableCopy”(例如,alloc、newObject或mutableCopy)开头的方法创建一个对象。
2、您可以使用retain(strong)获取对象的所有权:
通常保证接收到的对象在接收到的方法中保持有效,并且该方法还可以安全地将对象返回到其调用者。
3、当你不再需要它时,你必须放弃所有权:
通过发送release消息或autorelease消息来放弃对对象的所有权。
4、你不能放弃对你不拥有的物品的所有权
注意:
1、使用autoRelease延迟释放
2、你不拥有被引用返回的对象
3、当应用程序终止时,对象可能不会被发送dealloc消息。
由于进程的内存在退出时会自动清除,所以简单地允许操作系统清理资源比调用所有内存管理方法更有效。
4、CoreFoundation使用类似当不相同的规则
三、实用的内存管理
1、使用访问器方法使内存管理更容易:
使用方法构造器对实例变量赋值;
不要再init和dealloc方法使用方法构造器
2、使用weak避免引用循环
3、避免使用中的对象被释放
4、不要使用dealloc来管理稀缺资源
如果不这样的话,会导致以下问题
1、tear-down机制本质上是无序的,如果对象被意外释放执行dealloc
例如,tear-down顺序可能会改变,这可能会导致意外的结果。
2、稀缺资源没有被释放
内存泄漏是应该修复的错误,但它们通常不会立即致命。
然而,如果当你期望稀缺资源被释放时,稀缺资源没有被释放,你可能会遇到更严重的问题。
例如,如果应用程序耗尽了文件描述符,用户可能无法保存数据。
3、清理逻辑在错误的线程上执行。
如果一个对象是在一个意外的时间自动释放的,它将被分配在任何线程的自动释放池块上。
如果它恰好是在对于只应从一个线程中触及的资源来说,这很容易是致命的。
5、集合拥有它们所包含的对象(array,dic,set)
添加到集合中时,对象的引用计数+1。
从集合中移除,或者集合本身被释放时,对象的引用计数-1。
四、使用AutoreleasePool
1、关于autoreleasePool
autoreleasePool提供了一种机制,你可以丢掉对一个对象的所有权,但是不会导致对象立即被释放掉。
使用方式:
@autoreleasepool {
// Code that creates autoreleased objects.
}
在autoreleasepool结束的时候,在block块里面被标记为autorelease的对象会收到一次release消息。
autoreleasepool可以嵌套使用。
AppKit和UIKit在每次runloop时,都会放到autoreleasepool的block中使用。所以一般来说不用显性的手动创建。
除非以下几种情况:
1、如果你没有基于UI framework写程序,如一个command-line工具
2、你写了一个循环,创建了很多的临时对象
此时你需要创建一个autoreleasePool,在每次循环结束的时候释放掉临时变量
3、你创建了一个辅助线程(貌似只有旧版本会有问题)
2、使用本地自动释放池块减少峰值内存足迹
例子:
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
}
}
三、Autorelease Pool Block和线程
Cocoa应用程序中的每个线程都维护自己的Autorelease Pool Block堆栈。如果您正在编写一个只有Foundation-only的程序,或者如果分离一个线程(没有使用GCD或者NSThread),则需要创建自己的Autorelease Pool Block。
如果您的应用程序或线程寿命很长,并且可能生成大量自动释放的对象,则应该使用自动释放池块(如AppKit和UIKit在主线程上做);否则,自动释放的对象会积累,内存占用空间也会增加。如果分离的线程不进行Cocoa调用,则不需要使用自动释放池块。
四、NSCopying
NSCOpying协议声明了一种提供对象功能副本的方法。
“副本”的确切含义可能因类而异,但副本必须是一个功能独立的对象,其值与复制时的原始值相同。
用NSCOpying制作的副本由发送者隐式保留,发送者负责释放它。
声明一个方法,copyWithZone:,但是复制通常使用方便方法副本调用。复制方法是为所有NSOBjects定义的,并简单地调用具有默认区域的copyWithZone:。
网友评论