赞
踩
【iOS开发】—— 自动引用计数初步学习
【iOS开发】——引用计数的简单了解
在引用计数架构下,对象有个计数器,用以表示当前有多少个事物想令此对象继续存活下去。在Objective-C
中叫做“保留计数”,也可以叫做“引用计数”。
NSObject协议声明了以下三个方法用来操作计数器:
Retain 递增保留计数
release 递减保留计数
autorelease 待稍后清理“自动释放池”时,再递减保留计数
注意
:有时调用完release
方法后,为避免在不经意间使用了无效对象,要清空指针,比如:
[number release];
number = nil;
这样就能保证不会出现可能指向无效对象的“悬挂指针”。
调用retain
方法可以保留对象。若属性为“strong
”关系,则设置的属性值会保留。比如,有个名叫foo的属性由名为_foo的实例对象所实现,那么该属性的设置方法会是这样:
- (void)setFoo:(id)foo {
[foo retain];
[_foo release];
_foo = foo;
}
此方法就是保留新值,释放旧值,然后更新实例变量,令其指向新值。注意顺序。
调用autorelease
会在稍后递减计数,通常是在下一次“事件循环”时递减,也可能更早些。此方法在返回对象时更应该使用它,比如:
- (NSString*)stringValue {
NSString* str = [[NSString alloc] initWithFormat:@"I am this : %@", self];
return str;
此时返回的str对象其保留计数比期望值要多一,因为调用alloc
会令保留计数加一,而其后又没有与之对应的释放操作。所以要设法将其减1。但是不能在方法内释放str
,否则还没等方法返回,系统就把该对象回收了。这里我们就可以使用autorelease
。
- (NSString*)stringValue {
NSString* str = [[NSString alloc] initWithFormat:@"I am this : %@", self];
return [str autorelease];
这样stringValue
方法把NSString
对象返回给调用者时,此对象必然存活。并且返回的str
对象将于稍后自动释放。
由此可加:autorelease能延长对象生命期,使其在跨越方法调用边界后仍然可以存活一段时间。
使用引用计数机制时,经常要注意一个问题就是“保留环”,也就是成环状相互引用的多个对象,这将导致内存泄漏,因为循环会导致对象其保留计数不会降为0。
在垃圾收集环境中,通常将这种情况视为“孤岛”,此时,垃圾收集器会把这三个对象都回收走。而在Objective-C
的引用计数架构中,则无法享受到这一便利。通常采用“弱引用”来解决此问题,或是从外界命令循环中的某个对象不再保留另外一个对象。
要点:
1.由于ARC会自动执行retain、release、autorelease、dealloc
等操作,所以在ARC下直接调用这些方法是非法的。
2. 不能重写retain、release、autorelease
,因为这些方法不会被直接调用,ARC会调用与其等价的底层函数。
归调用者所有意思是:调用上述四个词语开头的方法的那段代码要负责释放方法所返回的对象,也就是说,这些对象的保留计数计数是正值,而调用了这四种方法的那段代码要将其中一次保留操作抵消掉。
若方法名不以上述四个词语开头,则表示其所返回的对象并不归调用者所有。
objc_autoreleaseReturnValue
函数,把retain
方法改为objc_retainAutoreleaseReturnValue
。下面用一段代码掩饰ARC
是如何通过特殊函数来优化程序的:为了求得最佳效率,这些特殊函数的实现代码都因处理器而异。下面这段伪代码描述了其中的步骤:
将内存管理交由编译器和运行期组件来做,可以使代码得到多种优化。
ARC也会处理局部变量与实例变量的内存管理。默认情况下,每个变量都是指向对象的强引用。
_ _strong
:默认语句,保留此值。_ _unsafe_unretained
:不保留此值,这么做可能不安全,因为等到再次使用变量时,其对象可能已经回收了。_ _weak
:不保留此值,但是变量可以安全使用,因为如果系统把这个对象回收了,那么变量也会清空。_ _autoreleasing
:把对象“按引用传递”给方法时,使用这个特殊的修饰符。此值在方法返回时自动释放。ARC
可以对实例变量进行内存管理,要管理器内存,ARC就要在“回收分配给对象的内存”时生成必要的清理代码。凡是具备强引用的变量,都必须释放,ARC会在dealloc
方法中插入这些代码。ARC
会借用Objective-C++
的一些特性来生成清理例程。回收Objective-C++
对象时,待回收的对象会调用所有C++
对象的析构函数。编译器如果发现某个对象含有C++
对象,就会生成名为.cxx_destruct
的方法。而ARC
借助此特性,在该方法中生成清理内存所需的代码。如果存在非Objective- C
对象,ARC会在生成的清理内存代码中调用超类的dealloc
方法。
ARC会自动生成回收对象时回收对象所执行的代码,所以通常无须再编写dealloc
方法。
不使用ARC时,可以覆写内存管理方法。但使用ARC环境时,不可以覆写,以避免干扰到ARC分析对象生命期的工作。
要点:
Objective-C
对象的内存。尤其要注意:CoreFoundation
对象不归 ARC管理,开发者必须适时调用CFRetain/CFRelease
。应该在dealloc方法中做些什么呢?主要就是释放对象所拥有的引用,其次把原来配置过的观测行为都清理掉。比如CoreFoundation
对象必须手工释放,用NSNotificationCenter
给对象订阅过某种通知,那么也要注销,否则继续给回收后的对象发通知将会导致程序崩溃。所以dealloc方法可以这样来写:
- (void)dealloc {
CFRelease(coreFoundationObject);
[[NSNotificationCenter defaultCenter] removeObserve:self];
注意
:如果手动管理引用计数而不使用ARC的话,那么最后还需调用“[super dealloc]
”。ARC会自动执行此操作,所以一般不选择手动管理,因为手动管理需要将所拥有的全部Objective-C对象逐个释放。
要点:
NSNotificationCenter
等通知,不要做其他事情。dealloc
里调用;只能在正常状态下执行的那些方法也不应在dealloc
里调用,因为此时对象已处于正在回收的状态了。Objective-C的错误模型表明,异常只应该发生严重错误后抛出,不过有时仍然需要编写代码来捕获并处理异常。
先介绍一下@try、@catch、@finally:
@try {
... 逻辑处理
...执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容
}
@catch {
... 异常捕捉
...除非try里面执行代码发生了异常,否则这里的代码不会执行
}
@finally{
... 逻辑执行结果
...不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally
}
在@try
块中如果先保留了某个对象,然后在释放它之前又抛出了异常,除非@catch
块能处理此问题,否则对象所占内存就将泄漏。
例如下面这段代码:
如果doSomethingThatMayThrow
抛出异常,由于异常会令执行过程终止并跳至catch块,导致之后的[object release]
代码不会执行,这就回导致内存泄漏。解决办法就是使用@finally块,无论是否抛出异常,都会执行其中的代码,所以将上面的代码改为下面这样:
如果要手动管理引用计数,而且必须捕获异常,那么就要设法保证所编代码能把对象正确清理干净。若使用ARC且必须捕获异常,则需打开编译器的-fobjc-arc-exceptions
标志。
要点:
在对象图里,会经常出现一种情况,就是几个对象之间都以某种方式相互引用,从而形成“环”,这种情况下就会造成内存泄漏。避免保留环最佳方式就是弱引用。这种引用通常来表示“非拥有关系”。将属性声明为unsafe_unretained
即可。但是会因为属性特质中的unsafe_unretained
一词表明属性值可能不安全,而且不归此实例所有,在修饰的属性所指的那个对象被回收后,再在其上调用方法就会使应用程序崩溃。但是OC还有一项与ARC相伴的运行期特性,可以令开发者安全的使用弱引用:这就是weak
属性特质。它与unsafe_unretained
的作用完全相同。然而只要系统把属性回收,属性值就会设为nil
。
要点:
有一项特性叫做“自动释放池”,释放对象有两种方式:一种是调用release
方法,使其保留计数立即递减;另一种是调用autorelease方法,将其加入自动“释放池”中。自动释放池用于存放那些需要在稍后某个时刻释放的对象。清空释放池,系统就会向其中的对象发送release
消息。
创建自动释放池所用语法:
@autoreleasepool {
//...
}
要点:
Cocoa提供了“僵尸对象”这个方便的功能,在运行期系统会把所有已经回收的实例转化成特殊的“僵尸对象”,而不会真正回收它们。僵尸对象收到消息后,会抛出异常,因此僵尸对象是调试内存管理问题的最佳方式。
要点
NSObject协议中定义了下列方法,用于查询对象之前的保留计数:
- (NSUInteger)retainCount
然而ARC已经将此方法废弃了。所以不要再去使用retainCount就好了。
要点:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。