现在iOS开发已经是arc(系统自动管理),不用自己release操作。
ios没有java那样的垃圾回收机制来管理内存,而是通过对象引用计数来管理内存的。
1 引用计数
ObjC采用引用计数(reference counting)的技术来进行管理: 1)每个对象都有一个关联的整数,称为引用计数器 2) 对象被创建时,则将对象的 引用计数加1;当使用该对象时,则将对象的引用计数加1 3)当结束使用该对象时,则将对象的引用计数减1 4)当引用计数的值变为0时,表示对象没有被使用,此时对象将被释放。1.当对象被创建(通过alloc、new或copy等方法)时,其引用计数初始值为1
2.給对象发送retain消息,其引用计数加1
3.給对象发送release消息,其引用计数减14.給对象发送assign消息,只是赋值操作,其引用计数不受影响。5.当对象引用计数归0时,ObjC給对象发送dealloc消息销毁对象//创建cat对象,cat引用计数+1Cat * cat = [Cat alloc]init];//cat引用计数+1[cat retain];//cat引用计数-1[cat release];//cat引用计数-1[cat release];//cat引用计数==0,将cat对象指针置nil,否则会变成野指针。cat = nil;
2 释放池(@autoreleasepool)
释放池(@ autoreleasepool)创建对象,区别是对象不会立刻释放,只有释放池释放后对象才会被释放。所以在释放池未释放的过程中,里面的对象任然存在。使用自动释放池需要注意:
1.自动释放池实质上只是在释放的时候給池中所有对象对象发送release消息,不保证对象一定会销毁,如果自动释放池向对象发送release消息后对象的引用计数仍大于1,对象就无法销毁。
2.自动释放池中的对象会集中同一时间释放,如果操作需要生成的对象较多占用内存空间大,可以使用多个释放池来进行优化。比如在一个循环中需要创建大量的临时变量,可以创建内部的池子来降低内存占用峰值。
3.autorelease不会改变对象的引用计数
ARC提供四种修饰符,分别是__strong, __weak, __autoreleasing, __unsafe_unretained注意: 1)从MRC转换成ARC,可以更好地开发维护项目 2)如果工程引用了不支持ARC的库或者文件,可以在Build Phases的Compile Sources将对应的m文件的编译器参数配置为-fno-objc-arc 3)ARC能帮我们简化内存管理问题,但不代表它是万能的,还是有它不能处理的情况,这就需要我们自己手动处理,比如循环引用、非ObjC对象、Core Foundation中的malloc()或者free()等等
__strong:强引用,持有所指向对象的所有权,无修饰符情况下的默认值。如需强制释放,可置nil。
NSTimer * timer = [NSTimer timerWith...];
相当于
NSTimer * __strong timer = [NSTimer timerWith...];
当不需要使用时,强制销毁定时器
[timer invalidate];
timer = nil;
__weak:弱引用,不持有所指向对象的所有权,引用指向的对象内存被回收之后,引用本身会置nil,避免野指针。用于在block块语句以及代理防止循环引用。
比如避免循环引用的弱引用声明:
__weak __typeof(self) weakSelf = self;
__autoreleasing:自动释放对象的引用,一般用于传递参数
_unsafe_unretained:和__weak 一样,唯一的区别便是,对象即使被销毁,指针也不会自动置空, 此时指针指向的是一个无用的野地址。如果使用此指针,程序会抛出 BAD_ACCESS 的异常。
循环中对象占用内存大
for (int i = 0; i < 10000; i ++) {
Person * soldier = [[Person alloc]init];
[soldier fight];
}
该循环内产生大量的临时对象,直至循环结束才释放,可能导致内存泄漏,解决方法和上文中提到的自动释放池常见问题类似:在循环中创建自己的autoReleasePool,及时释放占用内存大的临时变量,减少内存占用峰值。
for (int i = 0; i < 10000; i ++) {
@autoreleasepool {
Person * soldier = [[Person alloc]init];
[soldier fight];
}
}
然而有时候autoReleasePool也不是万能的:
例子:假如有2000张图片,每张1M左右,现在需要获取所有图片的尺寸,你会怎么做?
如果这样做
for (int i = 0; i < 2000; i ++) {
CGSize size = [UIImage imageNamed:[NSString stringWithFormat:@"%d.jpg",i]].size;
//add size to array
}
用imageNamed方法加载图片占用Cache的内存,autoReleasePool也不能释放,对此问题需要另外的解决方法,当然保险的当然是双管齐下了
for (int i = 0; i < 2000; i ++) {
@autoreleasepool {
CGSize size = [UIImage imageWithContentsOfFile:filePath].size;
//add siez to array
}
}