以 '自动释放池块' 降低内存峰值
OC 对象的生命期取决于其 引用计数, 在 OC 的引用计数架构中,有意向特性叫做 '自动释放池',释放对象有两种方式, 一种是调用 release 方法, 使其保留计数立即递减, 另一种是调用 autorelease 方法, 将其加入 '自动释放池'中, 自动释放池用于存放那些需要在稍后某个时刻释放的对象,
不要使用 retainCount
OC 通过 引用计数来管理内存,每个对象都有一个计数器,其值表明还有多少个其他对象想令此对象继续存活,对象创建好之后,其保留计数大于 0. 保留与释放操作分别会使该计数递增或递减,当计数变为 0 时,对象就为系统所回收并摧毁了.
NSObject 协议中定义了下列方法,用于查询对象当前的保留计数
- (NSUInterger) retainCount;
然而 ARC 已经将此方法废弃了,实际上,如果在 ARC中调用, 编译器就会报错, 这和在 ARC 中调用 retain,release,autorelease 方法时的情况是一样的,虽然此方法已经废弃了, 但是还是经常有人误解它,其实这个方法在 非 ARC 环境下还是可以用的,
这个方法看上去似乎非常合理, 挺有用的,它返回了保留计数,而此值对每个对象来说显然都很重要,但是问题在于,保留计数的绝对数值一般斗鱼开发者所留意的事情完全无关, 即使只是在调试环境下调用, 通常还是无所助益的.
此方法之所以无用,其首要原因在于: 它所返回的保留计数只是某个给定时间点上的值, 该方法并未考虑到系统会稍后把自动释放池清空, 因而不回将后续的释放操作从返回值里减去, 这样的话, 此值就未必能真实反映实际上的保留计数,因此.下面这种写法非常糟糕
while ([object retaiCount]){
[object release];
}
这种写法的第一个错误时: 它没有考虑到后续的自动释放操作, 只是不停的通过释放操作来降低保留计数, 直至对象为系统所回收, 加入此对象也在自动释放池中,那么稍后系统清空池子时还要把它在释放一次, 而这将导致程序崩溃.
第二个错误是: retainCount 可能永远不返回 0,因为有时候系统会优化对象的释放行为, 在保留计数还是 1 的时候就把它释放了. 只有在 系统不打算优化时,计数值才会递减至 0, 因此,保留计数可能影院都互惠完全归零, 所以说, 这段代码就算有时能正常运行, 也多半是凭借运气, 而非理性判断, 对象回收之后,如果 while 循环继续运行, 那么目前的运行期系统一般会导致程序直接崩溃.
从来都不需要写这种代码,这段代码所要实现的操作,应该通过内存管理来解决, 开发者在期望系统与某处回收对象时,应该确保没有尚未抵消的保留操作,也就是不要令宝皮裤计数大于期望值, 在这种情况下,如果发现某处对象的内存泄漏, 那么应该检查还有谁仍然保留这个对象, 并查明其为何没有释放此对象.
有时候可能还是想看一下保留计数的具体值, 然而看过之后你就会觉得奇怪了. 它的值为何这么大呢?
NSString* atring = @"Some String";
NSLog(@"string retainCount = %lu",[string retainCount]);
NSNumber *numberI = @1;
NSLog(@"numberI retainCount = %lu",[numberI retainCount]);
NSNumber *numberF = @3.14f;
NSLog(@"numberF retainCount = %lu",[numberF retainCount]);
输出的消息如下:
string retainCount = 18446744073709551615
numberI retainCount = 9223372036854775807
numberF retainCount = 1
前两者皆为 '单例对象',所以其保留计数都很大,系统会尽可能把 NSString 实现成单例对象, 像这种单例对象,其保留计数绝对不会变, 这种对象的保留及释放操作 都是 '空操作(no-op)', 即便两个单例对象之间,其保留计数也各不相同, 系统对其保留计数的这种处理方式再一次证明: 我们不应该总是依赖保留计数的具体值来编码.
总结:
对象的保留计数看似有用,实则不然, 因为任何给定时间点上的 '绝对保留计数' 都无法反映对象生命期的全貌.
引入 ARC之后,retainCount 方法就正式废止了. 在 ARC 下调用该方法会导致编译器报错.