Reputation: 1144
Coming from a different language I got surprised by a silent bug where an object which is passed to a method as a callback suddenly is never called. The reference to a callback is somehow lost.
Minimal (not runnable) example of a problem:
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
}
}
Minimal (not runnable) example of a "solution":
class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
out.startRecording(to: /*...*/, recordingDelegate: self)
//Result: delegate methods are called when approperiate
}
}
I'm puzzled...
This question stems from the AVCaptureMovieFileOutput never calls delegate on screen recording
Upvotes: 1
Views: 50
Reputation: 1505
Most delgates are weak
so that they do not create a retain cycle, see Automatic Reference Counting (ARC). Most of this is assuming that the delegate storage for what you are using is weak
.
In your first example the only strong reference to the object is held by the function bar
, as the delegate is a weak reference. Once the function ends, the only remaining strong reference is gone and the object is free to be deleted.
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.
}
In the second example when using self
as the delegate, self
is likely sticking around after the end of the bar
function, so the delegate remains.
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!
}
Hopefully that answers the "why."
As for prevention, you need to make sure you keep a strong reference to any delegates (or weak variables) that you want to stay alive.
This behavior is by design as it is used to prevent retain cycles and memory leaks. When designing your own classes with delegates it is up to you to use weak
appropriately to prevent retain cycles when necessary.
As for the silence of the failure, there are many cases where the delegates are optional, and it is not considered a failure for the delegate to be nil and for the delegate functions to not get called. Many times the functions are called like delegate?.delegateMethod()
intentionally so that the function will be called if you want to have a delegate and it won't cause a problem if you don't want to have a delegate.
Upvotes: 1