This function is supposed to re-schedule the execution of a work item:
class MyClass {
var id: String?
var workItem: DispatchWorkItem?
var isDoing = false
func myFunction() {
guard let id = self.id else { return }
isDoing = true
NotificationCenter.default.post(name: MyNotification, object: nil, userInfo: ["id": id])
workItem?.cancel()
workItem = DispatchWorkItem {
self.isDoing = false
NotificationCenter.default.post(name: MyNotification, object: nil, userInfo: ["id": id])
}
if let workItem = workItem {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(10), execute: workItem)
}
}
}
It works fine in development, but the design seems suspicious, so I ask some basic questions:
workItem
be nil, if workItem?.cancel()
is called just before the queue tries to execute the workItem
?id
inside workItem
ever be nil when workItem
is executed or is it retained by the scoped let id = self.id
?isDoing
inside workItem
be already deallocated when the workItem
is executed if the MyClass
object has been deallocated? In other words, what happens to the scheduled workItem
when the MyClass
object is deallocated?Not sure what you mean. You don't nil out workItem
anywhere.
No, it can't be nil
since you're working with a local variable - a copy of self.id
. By using guard
you make sure that local variable id
is not nil, and closure keeps a strong reference (by default) to captured values, so it won't be deallocated.
isDoing
is an instance variable of MyClass
so it can't be deallocated before MyClass
instance is deallocated. The problem is, in your case MyClass
can't be deallocated because you're looking at a strong reference cycle. The closure, by default, keeps a strong reference to the value captured, and you are capturing self
. And since self
keeps a strong reference to workItem
, which in turn keeps a strong reference to closure that captures self
, hence the reference cycle.
In general, when capturing self
you use a capture list to work with a weak reference to self
and do a check whether it hasn't been deallocated
workItem = DispatchWorkItem { [weak self] in
guard let strongSelf = self else { return }
// do stuff with strongSelf
}