У меня есть протокол:
enum DataFetchResult {
case success(data: Data)
case failure
}
protocol DataServiceType {
func fetchData(location: String, completion: (DataFetchResult) -> (Void))
func cachedData(location: String) -> Data?
}
С примером реализации:
/// An implementation of DataServiceType protocol returning predefined results using arbitrary queue for asynchronyous mechanisms.
/// Dedicated to be used in various tests (Unit Tests).
class DataMockService: DataServiceType {
var result : DataFetchResult
var async : Bool = true
var queue : DispatchQueue = DispatchQueue.global(qos: .background)
var cachedData : Data? = nil
init(result : DataFetchResult) {
self.result = result
}
func cachedData(location: String) -> Data? {
switch self.result {
case .success(let data):
return data
default:
return nil
}
}
func fetchData(location: String, completion: (DataFetchResult) -> (Void)) {
// Returning result on arbitrary queue should be tested,
// so we can check if client can work with any (even worse) implementation:
if async == true {
queue.async { [weak self ] in
guard let weakSelf = self else { return }
// This line produces compiler error:
// "Closure use of non-escaping parameter 'completion' may allow it to escape"
completion(weakSelf.result)
}
} else {
completion(self.result)
}
}
}
Приведенный выше код скомпилирован и работает в Swift3 (Xcode8-beta5), но больше не работает с бета 6. Можете ли вы указать мне на причину?
swift
swift3
closures
xcode8-beta6
Лукаш
источник
источник
Ответы:
Это связано с изменением поведения по умолчанию для параметров типа функции. До Swift 3 (в частности, сборки, поставляемой с Xcode 8 beta 6), по умолчанию они будут экранироваться - вам нужно будет пометить их
@noescape
, чтобы предотвратить их сохранение или захват, что гарантирует, что они не переживут длительность вызова функции.Тем не менее, теперь
@noescape
это значение по умолчанию для параметров с типом функции. Если вы хотите сохранить или записать такие функции, вам нужно пометить их@escaping
:См. Предложение Swift Evolution для получения дополнительной информации об этом изменении.
источник
async
параметр функции (и, следовательно,completion
функция) будет вызван доfetchData
выхода - и, следовательно, должен быть@escaping
.@escaping
параметр в требовании протокола с@escaping
параметром в реализации этого требования (и наоборот для неэкранирующих параметров). То же самое было в Swift 2 для@noescape
.Поскольку @noescape используется по умолчанию, есть 2 варианта исправления ошибки:
1) как указал @Hamish в своем ответе, просто пометьте завершение как @escaping, если вы действительно заботитесь о результате и действительно хотите его избежать (это, вероятно, имеет место в вопросе @ Lukasz с юнит-тестами в качестве примера и возможностью асинхронности) завершение)
ИЛИ
2) сохранить стандартное поведение @noescape, сделав необязательное завершение необязательным, полностью отбрасывая результаты в тех случаях, когда вы не заботитесь о результате. Например, когда пользователь уже "ушел" и контроллер вызывающего представления не должен зависать в памяти только потому, что произошел какой-то небрежный сетевой вызов. Так же, как и в моем случае, когда я пришел сюда в поисках ответа, и пример кода не был очень уместен для меня, поэтому пометка @noescape была не лучшим вариантом, хотя с первого взгляда это звучало как единственное.
источник