在函数中创建的对象尽管被进一步传递,但会消失



来自不同的语言,我对一个无声的错误感到惊讶,其中突然从不调用作为回调传递给方法的对象。对回调的引用以某种方式丢失。

问题的最小(不可运行(示例:

class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
let delegate = Foo() //nonsensical in this case, in normal case diff. object will be used
out.startRecording(to: /*...*/, recordingDelegate: delegate)
//Result: delegate methods are never called
}
}

"解决方案"的最小(不可运行(示例:

class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
out.startRecording(to: /*...*/, recordingDelegate: self)
//Result: delegate methods are called when approperiate
}
}

我很困惑...

  • 为什么会这样?
  • 如何防止这种情况的发生?
  • 这种无声的失败是设计出来的吗?

这个问题源于AVCaptureMovieFileOutput从不调用屏幕录制代表

大多数德尔盖特都是weak的,因此它们不会创建保留周期,请参阅自动引用计数 (ARC(。其中大部分是假设您正在使用的委托存储是weak.

在你的第一个例子中,对对象的唯一强引用是由函数bar持有的,因为委托是一个弱引用。函数结束后,唯一剩余的强引用将消失,并且可以自由删除对象。

class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
let delegate = Foo() //object created, and strong reference stored in variable called delegate
out.startRecording(to: /*...*/, recordingDelegate: delegate) // object passed in and likely stored in a weak variable inside of the `out` object. This means it will not keep a strong reference to your Foo object.
//Result: delegate methods are never called
}// local variable called delegate goes out of scope, strong reference to your Foo object goes away, there are no more strong references, can be deleted.
}

在第二个示例中,当使用self作为委托时,self可能会在bar函数结束后留下来,因此委托仍然存在。

class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
out.startRecording(to: /*...*/, recordingDelegate: self) // pass `self`, which presumably has something else referencing it with a strong reference, so it stays alive
//Result: delegate methods are called when approperiate
} // `self` still has strong references to it (somewhere else) keeping it alive after the function call, so the weak reference that is being used to call the delegate methods can still operate! Yay!
}

希望这能回答"为什么"。

至于预防,你需要确保对你想要保持活力的任何委托(或弱变量(的强引用。

此行为是设计使然,因为它用于防止保留周期和内存泄漏。在设计自己的带有委托的类时,您可以根据需要适当地使用weak以防止保留周期。

至于失败的静默,在许多情况下,委托是可选的,委托为零和委托函数不被调用并不被视为失败。很多时候,函数像故意delegate?.delegateMethod()一样调用,以便在您想要委托时调用该函数,如果您不想拥有委托,它不会引起问题。

最新更新