Почему опциональные замыкания в Swift являются escaping
10 июня 2018
https://www.jessesquires.com/blog/2018/06/10/why-optional-swift-closures-are-escaping/
В недавнем эпизоде подкаста мы с JP обсудили неявное escaping замыканий в Swift. По мере развития Swift поведение параметров замыканий в функциях по умолчанию изменилось. До Swift 3 параметры замыканий по умолчанию были escaping. После принятия SE-0103 поведение по умолчанию изменилось на non-escaping.
https://www.jessesquires.com/blog/2018/06/10/why-optional-swift-closures-are-escaping/
В недавнем эпизоде подкаста мы с JP обсудили неявное escaping замыканий в Swift. По мере развития Swift поведение параметров замыканий в функциях по умолчанию изменилось. До Swift 3 параметры замыканий по умолчанию были escaping. После принятия SE-0103 поведение по умолчанию изменилось на non-escaping.
В Swift 3, чтобы отказаться от поведения по умолчанию, вы могли аннотировать параметр функции с помощью @noescape. Теперь, когда это поведение стало стандартным, вам нужно указать @escaping, чтобы замыкание стало escaping. Грег Хео (Greg Heo) предоставляет отличное объяснение на Swift Unboxed.
В любом случае, наш эпизод был сосредоточен на недавно обнаруженной проблеме в компиляторе Swift, где замыкание с модификатором no-escape, переданное из Swift, могло стать escaping в Objective-C. Дуг Грегор (Doug Gregor) подробно объяснил проблему на форумах Swift. Во время нашего обсуждения в шоу была еще одна часть истории об "escaping замыканиях", в которой мы с JP были не уверены.
Оставив в стороне все эти правила и изменения, параметры опциональных замыканий не могут быть аннотированы, потому что они всегда неявно являются escaping. Но почему?
В Твиттере Дэвид Харт (David Hart) объяснил:
Нет смысла добавлять аннотации escaping к опциональным замыканиям, потому что они не являются типами функций: по сути, это enum (Optional), содержащий функцию, так же как вы бы хранили замыкание в любом типе. Оно неявно является escaping, потому что находится во владении другого типа.
Теперь мне это кажется таким очевидным, но, честно говоря, я не понимал, почему к опциональным замыканиям относятся иначе. Optionals — это всего лишь enum с двумя случаями. Как и любой другой тип, который содержит замыкание, это замыкание по определению является escaping. Таким образом, опциональное замыкание не сильно отличается от структуры, которая имеет свойство-замыкание:
typealias Handler = () -> Void
struct Closure {
let handler: Handler
}
Более того, нет смысла аннотировать нефункциональные типы как @escaping. Конечно, это довольно легко проверить самостоятельно:
func performWorkOptional(handler: Handler?) {
print(type(of: handler))
}
performWorkOptional { /* ... */ }
// выводит: Optional<() -> ()>
А для случая без опционала:
А для случая без опционала:
func performWork(handler: @escaping Handler) {
print(type(of: handler))
}
performWork { /* ... */ }
// выводит: () -> ()