J'ai un protocole:
enum DataFetchResult {
case success(data: Data)
case failure
}
protocol DataServiceType {
func fetchData(location: String, completion: (DataFetchResult) -> (Void))
func cachedData(location: String) -> Data?
}
Avec un exemple d'implémentation:
/// 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)
}
}
}
Le code ci-dessus compilé et fonctionnait en Swift3 (Xcode8-beta5) mais ne fonctionne plus avec beta 6. Pouvez-vous m'indiquer la cause sous-jacente?
swift
swift3
closures
xcode8-beta6
Lukasz
la source
la source
Réponses:
Cela est dû à une modification du comportement par défaut des paramètres de type de fonction. Avant Swift 3 (en particulier la version livrée avec Xcode 8 beta 6), ils s'échappaient par défaut - vous devrez les marquer
@noescape
pour éviter qu'ils ne soient stockés ou capturés, ce qui garantit qu'ils ne survivront pas à la durée. de l'appel de fonction.Cependant, c'est maintenant
@noescape
la valeur par défaut pour les paramètres de type fonction. Si vous souhaitez stocker ou capturer de telles fonctions, vous devez maintenant les marquer@escaping
:Consultez la proposition Swift Evolution pour plus d'informations sur ce changement.
la source
async
paramètre de fonction (et donc lacompletion
fonction) sera appelé avant lesfetchData
sorties - et doit donc l'être@escaping
.@escaping
paramètre dans une exigence de protocole avec un@escaping
paramètre dans l'implémentation de cette exigence (et vice versa pour les paramètres sans échappement). C'était la même chose dans Swift 2 pour@noescape
.Puisque @noescape est la valeur par défaut, il existe 2 options pour corriger l'erreur:
1) comme @Hamish l'a souligné dans sa réponse, marquez simplement la fin comme @escaping si vous vous souciez du résultat et que vous voulez vraiment qu'il s'échappe (c'est probablement le cas dans la question de @ Lukasz avec les tests unitaires comme exemple et la possibilité d'async achèvement)
OU
2) Conservez le comportement par défaut @noescape en rendant la complétion facultative en supprimant complètement les résultats dans les cas où vous ne vous souciez pas du résultat. Par exemple, lorsque l'utilisateur s'est déjà "éloigné" et que le contrôleur de vue appelant n'a pas à se bloquer en mémoire simplement parce qu'il y a eu un appel réseau imprudent. Tout comme c'était le cas dans mon cas lorsque je suis venu ici pour chercher une réponse et que l'exemple de code n'était pas très pertinent pour moi, donc marquer @noescape n'était pas la meilleure option, même si cela semblait être le seul du premier coup d'œil.
la source