jenox
(Christian Schnorr)
1
From my understanding, optional closures are always implicitly escaping because they are boxed in an Optional that could theoretically escape.
I am currently writing a function that takes a (non-optional) closure and forwards it to UITableView's performBatchUpdates(_:completion:). I was fully expecting to have to annotate the parameter as @escaping, but it compiles just fine without the annotation. If I write a method with the same signature as performBatchUpdates(_:completion:) and try to pass the closure to this method, however, the compiler correctly complains:
extension UITableView {
func f(updateData: () -> Void) {
// these two work
self.performBatchUpdates(updateData)
self.performBatchUpdates({ updateData() })
// these two don't
self._performBatchUpdates(updateData) // Cannot convert value of type '() -> Void' to expected argument type '(() -> Void)?'
self._performBatchUpdates({ updateData() }) // Escaping closure captures non-escaping parameter 'updateData'
}
// Same signature as UIKit's _performBatchUpdates(_:completion:)
// https://developer.apple.com/documentation/uikit/uitableview/2887515-performbatchupdates
func _performBatchUpdates(_ updates: (() -> Void)?, completion: ((Bool) -> Void)? = nil) {
}
}
What kind of magic is this? Are there some hidden annotations in UIKit's interface to allow for non-escaping optional closures?
1 Like
I think the first closure is annotated with NS_NOESCAPE (i.e. closure type is void (NS_NOESCAPE ^ _Nullable)(void))) hence no error.
1 Like
jenox
(Christian Schnorr)
3
Thanks for the reply, that's indeed the case.
There's no way to replicate that kind of interface in pure Swift, is there?
1 Like
I don't think so, but I think you can use withoutActuallyEscaping to pass the closure:
withoutActuallyEscaping(updateData) { self._performBatchUpdates($0) }
3 Likes