我在ARC中遇到了对象生命周期的奇怪行为。我把它缩小到这个例子:
//---------------------------------------------------------------------------------
@interface MyObject : NSObject
@end
@implementation MyObject
-(id)init
{
self = [super init];
if(!self) return nil;
NSLog(@" MyObject init %p", self);
return self;
}
-(void)dealloc
{
NSLog(@" MyObject dealloc %p", self);
}
@end
//---------------------------------------------------------------------------------
@implementation TLAppDelegate
-(MyObject *)createMyObject:(NSString *)unusedArg
{
return [MyObject new];
}
-(void)someOperation
{
NSLog(@" Entering someOperation");
MyObject* x = [self createMyObject:@"some message"];
NSLog(@" Exiting someOperation; x should be deallocated right after this...");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(@"entering applicationDidFinishLaunching");
[self someOperation];
[self someOperation];
NSLog(@"exiting applicationDidFinishLaunching");
}
@end
代码摘要:applicationDidFinishLaunching
连续两次调用someOperation
。 someOperation
创建一个本地对象
我期望在someOperation
返回时取消分配。
因此,这是我期望的输出:
entering applicationDidFinishLaunching
Entering someOperation
MyObject init 0x600000012cd0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cd0
Entering someOperation
MyObject init 0x600000012cb0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cb0
exiting applicationDidFinishLaunching
但这是我实际得到的输出:
entering applicationDidFinishLaunching
Entering someOperation
MyObject init 0x600000012cd0 <-- this object is retained until the end of the output!
Exiting someOperation; x should be deallocated right after this...
Entering someOperation
MyObject init 0x600000012cb0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cb0
exiting applicationDidFinishLaunching
MyObject dealloc 0x600000012cd0
为什么在applicationDidFinishLaunching
返回之前第一个对象一直保留?
据我所知,MyObject
实例不应该生活在someOperation
之外。
这不是泄漏,因为它在下一个范围内被解除分配。
感觉就像编译器内联someOperation
一样,将范围与调用者合并。但这也不是真的
(如果我错了,请纠正我),编译器不能内联Objective-C方法。
对于我的真实项目,这会在多个方面造成问题。首先在我们的日志记录类中,我们跟踪一些范围,但由于这种行为,以下日志:
Generating some list {
Calculating item #1 {
}
Calculating item #2 {
}
Calculating item #3 {
}
}
变成这个,这是没有意义的:
Generating some list {
Calculating item #1 {
Calculating item #2 {
Calculating item #3 {
}
}
}
}
此外,这意味着我们没有充分的理由继续留下太多的记忆。
有没有办法让这种行为更具可预测性?
请注意,如果我使用__attribute__((objc_precise_lifetime))
,则此行为不会更改;它对观察到的行为没有任何改变。
答案 0 :(得分:2)
MyObject
不会在someOperation
结束时发布,因为createMyObject
返回的对象会保留(创建时)和自动释放(返回时) )。
因此,someOperation
随后将该对象分配给x
,并按照您的预期保留并释放它,但仍有来自createMyObject
的自动释放引用,直到自动释放池被耗尽(通常在每个运行循环结束时发生)。
如果您不是从MyObject
获取createMyObject
,而是直接将其实例化为:
-(void)someOperation{
NSLog(@" Entering someOperation");
MyObject* x = [[MyObject alloc] init];
NSLog(@" Exiting someOperation; x should be deallocated right after this...");
}
不会有自动释放的引用挂起,所有内容都应该按照您的预期立即解除分配。
Martin R提出了关于ARC和命名约定的一个很好的观点。默认情况下,方法返回的对象由ARC保留/自动释放(如果没有保留,则立即为dealloc
。如果没有自动释放,则会泄漏)。< / p>
根据Cocoa命名约定,有一些方法可以返回一个“保留”对象 - 即一个具有+1保留计数的非自动释放对象。对于名称以alloc…
,copy…
,init…
,mutableCopy…
或new…
开头的特定方法,ARC将返回一个保留对象。其他所有东西都会返回一个自动释放的。