ARC意味着编译器负责内存管理,非ARC意味着你照顾好它,但在这两种情况下,内存管理的工作方式完全相同:
- 如果一个对象必须活下去,其保留计数器增加(这是
retain
一样)
- 如果不再需要一个对象,它是失去了保留计数器被引用之前下降(这就是
release
一样)
- 如果您正在使用对象来完成,但它一定不会死亡,例如因为您需要将其作为方法结果返回(并且您不想返回死对象),所以它必须添加到autorelease池中,以后将减少其保留计数(这就是
autorelease
所做的,就像说“在将来某个时间在该对象上调用release
”。)
- 新创建的对象的保留计数为
1
。
- 如果保留计数达到零,则释放该对象。
无论你自己做了一切还是编译器为你做,它都不起作用。编译之后,这些方法也被称为ARC,但使用ARC时,编译器会为您决定调用哪种方法。还有一些额外的魔法,例如在将方法返回给方法结果时,ARC并不总是必须添加对象到自动释放池,这通常可以被优化掉,但是你不需要关心,因为只有当调用者和被调用方法都使用弧;如果其中一个不是,那么使用正常的autorelease
(它仍然像以前一样在ARC中工作)。
您唯一需要注意的就是保留周期。无论您是否使用ARC,引用计数都无法处理保留周期。这里没有区别。
陷阱?小心免费桥接。一个NSString *
和一个CFStringRef
事实上是同样的事情,但ARC不知道CF世界,所以虽然ARC负责NSString
,但您必须照顾CFString
。使用ARC时,您需要告诉ARC如何桥接。上述
CFStringRef cfstr = ...;
NSString * nsstr = (__bridge_transfer NSString *)cfstr;
// NSString * nsstr = [(NSString *)cfstr autorelease];
代码表示“ARC,请采取CFString
对象的所有权,并采取尽快释放它,你用它做的关怀”。代码的行为与下面注释中显示的代码相似;所以小心,cfstr
应该至少有一个保留计数,ARC将至少释放一次,但尚未。反过来想:以上
NSString * nsstr = ...;
CFStringRef cfstr = (__bridge_retained CFStringRef)cftr;
// CFStringRef cfstr = (CFStringRef)[nsstr retain];
代码表示“ARC,请给我NSString
的所有权,我要发布一次,我用它做的关怀”。当然,你必须遵守这个承诺!在某些时候,您必须致电CFRelease(cfstr)
否则您将泄漏内存。
最后有(__bridge ...)
这只是一个类型转换,没有所有权转移。这种类型的转换是危险的,因为如果你试图保持转换结果,它可以创建悬挂指针。通常,在将ARC对象提供给期望CF对象的函数时使用它,因为ARC将确保该对象保持活动直到函数返回,例如,这始终是安全的:
doSomethingWithString((__bridge CFStringRef)nsstr);
即使ARC被允许在任何时间释放nsstr
作为线下无码有史以来访问它了,它肯定不会释放它之前这个函数返回和功能参数是由定义只能保证在函数返回前一直保持活动状态(如果函数想让字符串保持活动状态,它必须保留它,然后ARC在释放它之后不会释放它,因为保留计数不会变为零)。
大多数人似乎与正在通过ARC斗争的事情对象为void *
背景下,作为你有时不得不用旧的API,但是这实际上是在死的简单:
- (void)doIt {
NSDictionary myCallbackContext = ...;
[obj doSomethingWithCallbackSelector:@selector(iAmDone:)
context:(__bridge_retained void *)myCallbackContext
];
// Bridge cast above makes sure that ARC won't kill
// myCallbackContext prior to returning from this method.
// Think of:
// [obj doSomethingWithCallbackSelector:@selector(iAmDone:)
// context:(void *)[myCallbackContext retain]
// ];
}
// ...
- (void)iAmDone:(void *)context {
NSDictionary * contextDict = (__bridge_transfer NSDictionary *)context;
// Use contextDict as you you like, ARC will release it
// prior to returning from this method. Think of:
// NSDictionary * contextDict = [(NSDictionary *)context autorelease];
}
我必须真正的大对你一见钟情,一见不见。请考虑以下代码:
@implementation SomeObject {
id _someIVAR;
}
- (void)someMethod {
id someValue = ...;
_someIVAR = someValue;
}
此代码在ARC和非ARC中不相同。在ARC所有的变量都默认很强,所以在ARC这段代码的行为就像这段代码有:
@interface SomeObject
@property (retain,nonatomic) id someIVAR;
@end
@implementation SomeObject
- (void)someMethod {
id someValue = ...;
self.someIVAR = someValue;
}
分配someValue
将保留它,对象保持活着!在非ARC的代码会像这样:
@interface SomeObject
@property (assign,nonatomic) id someIVAR;
@end
@implementation SomeObject
- (void)someMethod {
id someValue = ...;
self.someIVAR = someValue;
}
注意的属性是不同的,因为伊娃的非ARC既不strong
或weak
,它们是什么,他们只是指针(在ARC是称为__unsafe_unretained
,此处的关键字是不安全)。
所以,如果你有直接使用ivars的代码,并且没有使用setter/getters的属性来访问它们,那么从非ARC到ARC的切换可能导致代码中的保留循环过去具有理智的内存管理。另一方面,从ARC移动到非ARC,类似这样的代码会导致悬挂指针(指向前一个对象的指针,但由于对象已经死亡,这些指向无处并且使用它们具有不可预知的结果),因为过去的对象在现在可能意外死亡之前保持活力。
这似乎是共识。谢谢你的帮助。 – TomSwift