ARC弱ivar在返回之前发布 - 在构建发布时,而不是调试

时间:2013-02-27 17:53:53

标签: ios objective-c automatic-ref-counting weak-references release-builds

我有一个懒惰地创建对象并将其存储为弱属性的类。其他类可能会请求此对象,但显然必须对其进行强引用以防止对象被释放:

// .h
@interface ObjectManager
@property(nonatomic, weak, readonly) NSObject *theObject;
@end

// .m
@interface ObjectManager ()
@property(nonatomic, weak, readwrite) NSObject *theObject;
@end

@implementation ObjectManager
- (NSObject *)theObject
{
    if (!_theObject) {
        _theObject = [[NSObject alloc] init];
        // Perform further setup of _theObject...
    }
    return _theObject;
}
@end

当方案是Xcode被设置为为Debug构建时,一切正常 - 对象可以调用objectManagerInstance.theObject并返回theObject

当该方案设置为发布版本时,theObject会返回nil

// Build for Debug:
NSObject *object = objectManagerInstance.theObject;
// object is now pointing to theObject.

// Build for Release:
NSObject *object = objectManagerInstance.theObject;
// object is now `nil`.

我的猜测是编译器通过查看访问器方法本身未进一步使用_theObject来优化我的代码,因此弱变量在返回之前被设置为nil。看来我必须在实际返回变量之前创建一个强引用,我只能想用块来做,但是会很混乱,我宁愿避免它!

我可以使用返回类型的某种关键字来阻止ivar这么快被禁止吗?

2 个答案:

答案 0 :(得分:8)

最有可能的是,DEBUG构建会导致对象在自动释放池中停留足够长的时间以使其“工作”,而RELEASE构建会导致优化器进行更多的控制流分析,从而消除自动释放聊天。

坦率地说,编译器没有在发布版本中发出警告说代码永远无法正常工作是一个错误(请提交一个很好,简洁的示例)!

您需要在对象中保留一个强大的某处引用,直到需要强引用才有机会获取引用。

我想知道这样的事情是否有效:

- (NSObject *)theObject
{
    NSObject *strongObject;
    if (!_theObject) {
        strongObject = [[NSObject alloc] init];
        _theObject = strongObject;
        // Perform further setup of _theObject...
    } else {
        strongObject = _theObject;
    }
    return strongObject;
}

即。上面的内容更类似于返回自动释放对象的工厂方法,而在内部维护弱引用。但是优化器可能太聪明了一半并打破了上述内容。

答案 1 :(得分:4)

你被优化器咬了。

由于_theObject是一个弱引用,系统可以自由地去掉它,并在没有保留的情况下将你的弱引用归零。但是不需要立即这样做。

在懒惰的实例化器中,新创建的对象从不保留。优化器看到了这一点,并说“哇!我可以随时将此引用归零!为什么我不这样做......现在!”在你知道它之前,你将返回零。

您要做的是将延迟实例化的对象分配给局部变量,以获得持久的强引用,该引用将持续到函数的范围。您还想使用objc_precise_lifetime注释告诉编译器您确实需要完整的范围。

有关标准see this page

的详细信息