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?
Your answer would seem to imply that this is not possible
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"