在ARC中,方法内的"self"是否存在弱引用问题?

6
我有一个偶尔会崩溃的方法。
-(void)foo{
    [self doSomething];
    [self.delegate didFinish];
    [self doSomethingElse];
}

-doSomething正常工作,然后我调用一个委托-didFinish。在-didFinish中,对这个对象的引用可能被设置为nil,在ARC下释放它。当该方法崩溃时,它会在-doSomethingElse上崩溃。我的假设是self在方法内部是强引用的,使函数能够完成。self是弱引用还是强引用?有没有相关文档?为什么要强制或弱化它? 编辑 受下面一些答案的启发,我进行了一些调查。在我的情况下,崩溃的实际原因是NSNotificationCenter在任何情况下都不会保留观察者。Mike Weller在下面指出,调用方法的调用者应该在调用对象时保留该对象,以防止我上面描述的情况,但似乎NSNotificationCenter忽略了这个问题,并始终保持对观察者的弱引用。换句话说:
-(void)setupNotification{
    //observer is weakly referenced when added to NSNotificationCenter
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotification:)
                                                 name:SomeNotification object:nil];
}

//handle the notification
-(void)handleNotification:(id)notification{
    //owner has reference so this is fine
    [self doSomething];
    //call back to the owner/delegate, owner sets reference to nil
    [self.delegate didFinish];
    //object has been dealloc'ed, crash
    [self doSomethingElse];
}

2
如果它是“weak”,那么“self”应该变成“nil”,并且向“nil”发送消息不会崩溃。所以我猜,两者都不是。 - kennytm
你有没有用NSLog来查看self.delegate的情况? - Desdenova
你应该发布更多的代码和堆栈跟踪,还有任何异常或错误发生的信息。委托是如何声明的? - Nicholas Hart
我对调试代码不感兴趣,因为它已经被修复了。我对知道类似Mike在下面提供的这个问题的答案很感兴趣。 - Saltymule
2个回答

10

self would be strong within a method, allowing the function to complete. Is self weak or strong?

self在ARC中既不强也不弱。假定调用者持有一个引用,而self是不安全的未保留对象

同样真实的是,在ARC下,self可以在其自己的方法中被-dealloc,这被认为是“未定义行为(或至少是危险的)”,对于你的程序来说这样做是不安全的。

为什么它要是强还是弱?

出于性能考虑,它是未保留的--避免了在绝大多数情况下不必要的引用计数增加/减少操作。即使执行了所有这些额外的引用计数操作,你的程序仍然会受到多线程程序或存在竞争条件(也是UB)时的这些问题的影响。因此,这是那些他们(正确地)认为不需要保护自己的极端边缘案例之一。

是否有相关文档资料?

当然有! :)


而且这被视为“未定义行为(或至少是危险的)”,如果您的程序这样做的话。你在说,如果坏事发生在你的程序中,那就是未定义行为。当然,但这并不有用。程序员怎么知道这可能会发生呢? - newacct
@newacct 这是链接文档中的一句话。语句的目的很明确(尽管我会省略“至少危险”的部分)。这不是偶然发生的。在问题被确定之前,它似乎是偶然发生的。该语句的目的是:当/如果您的程序允许此类情况发生时,请将其视为未定义行为。未定义行为在C语言中有非常具体的定义。因此,该语句用于指定此错误的严重程度(如果遇到)。了解其程序的程序员可以在开发过程中识别它。崩溃和僵尸也会有所帮助。 - justin
当一个对象没有任何强引用时,它将被释放。问题在于,任何方法调用都有可能导致当前对象的最后一个强引用被移除。因此,在这个定义下,任何包含方法调用的方法都是未定义行为(除非另有证明)?这将使“定义行为”变得毫无意义。 - newacct
不,我从未说过它是内存安全的。然而,如果你按照正确的编写方式来写,那么它应该是安全的,不是吗?我的意思是,self不是强引用使得即使使用了正确的Objective-C技术和ARC编写的代码,在方法执行中仍可能无意中导致self被释放,在这种情况下,你无法确定代码的哪个部分是“错误”的。 - newacct
将这个特定的形式变得安全会有很高的成本,并且只会帮助极少数的情况。我认为ARC比他们的ObjC-GC尝试更优秀,而且我认为他们在安全性/速度/兼容性/期望方面取得了良好的平衡。因此,考虑到所有依赖MRC的代码,我认为今天的方式是引入ARC的好方法——一个实际的妥协,与现有大部分代码兼容。 - justin
显示剩余3条评论

5

self既不是弱引用也不是强引用。如果您可以访问self,那么您处于方法调用的范围内,并且该方法调用是由某人通过他们必须拥有的引用执行的。只要在作用域内,就暗示self是一个有效的引用,并且暗示任何内存管理或所有权都由调用者处理。

通过弱引用调用方法时,ARC将保留对象,直到该方法调用完成(请参见此答案)。启用严格的编译器警告后,您实际上会被迫在发送任何方法到该引用之前创建一个强引用。

因此,根据定义,如果在对象上调用方法,则调用者必须已经拥有所有权,无需执行任何操作。

当然,可能会出现在已释放对象上调用方法的情况,但这是由于调用者代码错误造成的结果。


“而且方法调用是由某人通过他们必须拥有的引用执行的。只要自身有效,self 就被暗示为一个有效的引用,并且暗示任何内存管理或所有权都由调用者处理。“但是,在此方法中进行的某些操作可能会无意中破坏原始所有权关系。通常无法保证 self 在方法中间仍然指向有效对象。” - newacct
“因此,根据定义,如果在对象上调用方法,则调用者必须已经拥有所有权,无需执行任何操作。”并非如此。只有在调用开始时接收器有效才能得到保证;不能保证它在调用的整个过程中始终有效。 - newacct

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接