I've been studying the AVCam example and notice that everything pertaining to state transitions for the capture session is performed on a dedicated DispatchQueue. My question is this: Can I use an actor instead?
It depends on the nature of the delegate callbacks. If the delegate callbacks are all notifications, you can get away with just an actor. OTOH, if a delegate callback requires you to return a value, and that value depend on the state of your actor, then an actor that wraps a Dispatch queue is definitely the best option.
I’m not an expert on AVFoundation, so I’m going to cons up a standalone example. Imagine you have this old school type that uses Dispatch queues and delegates:
class Varnisher {
init(queue: DispatchSerialQueue) {
self.queue = queue
}
let queue: DispatchSerialQueue
weak var delegate: Delegate?
protocol Delegate: AnyObject {
func varnisherShouldUseGloss(_ varnisher: Varnisher) -> Bool
}
… the actual implementation …
}
It guarantees that all the delegate methods will be run on queue
. Now let’s say you want to wrangle this in an actor. Here’s how to do that:
actor VarnishCentral: Varnisher.Delegate {
init() {
let queue = DispatchSerialQueue(label: "VarnishCentral.queue")
self.queue = queue
self.varnisher = Varnisher(queue: queue)
self.useGlossForNextWaffle = true
self.varnisher.delegate = self
}
private let queue: DispatchSerialQueue
private let varnisher: Varnisher
private var useGlossForNextWaffle: Bool
nonisolated var unownedExecutor: UnownedSerialExecutor {
self.queue.asUnownedSerialExecutor()
}
nonisolated func varnisherShouldUseGloss(_ varnisher: Varnisher) -> Bool {
self.assumeIsolated { a in
a.shouldUseGloss()
}
}
private func shouldUseGloss() -> Bool {
defer { self.useGlossForNextWaffle.toggle() }
return self.useGlossForNextWaffle
}
}
Note the following:
-
The initialiser creates a Dispatch serial queue for use by both the actor and
varnisher
. -
The
unownedExecutor
property relies on that queue being a valid serial executor. -
The
varnisherShouldUseGloss(_:)
delegate method is marked asnonisolated
. -
It uses
assumeIsolated(_:)
to tell the compiler that it actually is isolated. This will trap isvarnisher
calls a delegate method on some other queue. -
With an actor in hand, the delegate bounces to
shouldUseGloss()
, which is a standard actor-isolated method. -
As an actor-isolated method,
shouldUseGloss()
can access and modify actor state directly. -
And this chain allows for the function result to go all the way back to the
varnisher
.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"