为啥在 Objective-C 中执行 alloc 和 init 在单独的语句中会导致对象根据 Xcode 静态分析器被释放?
Posted
技术标签:
【中文标题】为啥在 Objective-C 中执行 alloc 和 init 在单独的语句中会导致对象根据 Xcode 静态分析器被释放?【英文标题】:Why in Objective-C does doing alloc and init in separate statements cause the object to be released according to the Xcode static analyzer?为什么在 Objective-C 中执行 alloc 和 init 在单独的语句中会导致对象根据 Xcode 静态分析器被释放? 【发布时间】:2011-09-21 10:41:40 【问题描述】:我正在使用 Objective-C 迈出第一步,但在 XCode 4.1 中的静态分析器 (Product->Analyze) 中遇到了一个小问题,尽管令人困惑。我为有理数创建了一个简单的 Fraction 类,我像这样分配和初始化它,
Fraction* f = [[[ Fraction alloc ] initWithNumerator:3 withDenomimator:5]
autorelease];
[ f print ];
其中print
是一种仅使用NSLog
显示分数的方法,一切正常。但是,如果我将 alloc
/init
构造拆分为两个语句(我意识到这是非惯用的 - 我只是想了解机器)并使用手动 release
而不是 autorelease
给出:
Fraction* f = [ Fraction alloc ]; // 1. Method returns an Objective-C object with
// a +1 retain count (owning reference)
[ f initWithNumerator:3 withDenomimator:5]; // 2. Object released
[ f print ]; // 3. Reference-counted object is used after it is released
[ f release ];
程序似乎仍然可以正常运行,但 XCode 分析器在 cmets 中给出警告。为什么 XCode 认为 init
调用会导致对象被释放?
在我提出问题时进一步考虑这一点,我可以看到我的两个程序并不完全相同,因为在第一个 sn-p 中,我的指针 f
是调用 init
的结果,而在第二个 sn -p 它是alloc
的结果。所以将我的代码更改为,
Fraction* a = [ Fraction alloc ];
Fraction* f = [ a initWithNumerator:3 withDenomimator:5];
[ f print ];
[ f release ]; // or should it be [ a release ] ?
使其完全等效,静态分析器停止抱怨。那么init
是否有可能返回与从alloc
传递给它的指针不同的指针,而不仅仅是配置它已传递的内存?使用此代码,我应该将[ a release ]
与alloc
或[ f release ]
与init
配对吗?
【问题讨论】:
【参考方案1】:那么,init 是否有可能返回一个与从 alloc 传递给它的指针不同的指针,而不仅仅是配置它已传递的内存?
绝对。
使用此代码,我应该将 [ a release ] 与 alloc 或 [ f release ] 与 init 配对吗?
您将初始化对象的值分配给f
(就像您一样)。此时,a
可能是一个悬空指针(如果返回另一个地址)。因此,f
应该是 release
d。
对这个顺序的解释是,对象可能选择返回自己的特殊版本/变体,并且这种重新分配发生在 init...
链上。
愚蠢的示范:
@interface Fraction : NSObject
@private
int numerator;
int denominator;
@end
static Fraction* EvilFraction = ...;
@implementation Fraction
- (id)initWithNumerator:(int)num denominator:(int)den
self = [super init];
if (nil != self)
if (0 == den)
[self release];
return [EvilFraction retain];
return self;
@end
【讨论】:
【参考方案2】:根据+[NSObject alloc]
的文档,Apple 明确建议不要尝试分离对 +alloc
和 -init
的调用:
必须使用 init... 方法来完成初始化过程。 例如:
TheClass *newObject = [[TheClass alloc] init];
和-[NSObject init]
:
在某些情况下,init 方法可能会释放新对象并返回 一个替代品。因此程序应该总是使用返回的对象 通过 init,不一定是 alloc 返回的那个 allocWithZone:,在后续代码中。
静态分析器在抱怨,因为它预计 +alloc
将与 -init
配对。因为-init
可以释放发送者,因此静态分析器认为您正在尝试调用已释放实例上的方法。
【讨论】:
【参考方案3】:那么
init
是否有可能返回与从alloc
传递给它的指针不同的指针,而不仅仅是配置它已传递的内存?
是的,就是这样。这也是您不只是在初始化程序中调用[super init]
的原因;相反,您调用它并将结果分配给self
,因为您不一定要返回相同的实例。
相关文档在The Objective-C Programming Language。
使用此代码,我应该将
[ a release ]
与alloc
或[ f release ]
与init
配对吗?
如果你真的想做这样的事情,你必须先检查它们是否不相等,否则你会过度释放。但真正的解决方案是不要拆分alloc
和init
。这是在 Objective C 中实例化对象的惯用方式,而以一种不同的方式来实现,你必须添加额外的代码,这会适得其反。
【讨论】:
以上是关于为啥在 Objective-C 中执行 alloc 和 init 在单独的语句中会导致对象根据 Xcode 静态分析器被释放?的主要内容,如果未能解决你的问题,请参考以下文章
从 Objective-C 里的 Alloc 和 AllocWithZone 谈起
《Objective-C 高级编程》 1.2.3节 alloc/retain/release/dealloc 实现——学习总结
Objective-C [[NSXMLDocument alloc] initWithContentsOfURL:url options:0 error:&error];帮助