Ich habe ein Protokoll:
enum DataFetchResult {
case success(data: Data)
case failure
}
protocol DataServiceType {
func fetchData(location: String, completion: (DataFetchResult) -> (Void))
func cachedData(location: String) -> Data?
}
Mit einer Beispielimplementierung:
/// 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)
}
}
}
Der obige Code wurde in Swift3 (Xcode8-beta5) kompiliert und funktioniert, funktioniert aber nicht mehr mit Beta 6. Können Sie mich auf die zugrunde liegende Ursache hinweisen?
swift
swift3
closures
xcode8-beta6
Lukasz
quelle
quelle
Antworten:
Dies ist auf eine Änderung des Standardverhaltens für Parameter des Funktionstyps zurückzuführen. Vor Swift 3 (speziell dem Build, der mit Xcode 8 Beta 6
@noescape
geliefert wird ) werden sie standardmäßig entkommen - Sie müssten sie markieren, um zu verhindern, dass sie gespeichert oder erfasst werden, was garantiert, dass sie die Dauer nicht überleben des Funktionsaufrufs.Dies ist jedoch jetzt
@noescape
die Standardeinstellung für funktionsbezogene Parameter. Wenn Sie solche Funktionen speichern oder erfassen möchten, müssen Sie sie jetzt markieren@escaping
:Weitere Informationen zu dieser Änderung finden Sie im Swift Evolution-Vorschlag .
quelle
async
Funktionsparameter (und damit diecompletion
Funktion) vor demfetchData
Beenden aufgerufen wird - und dies auch sein muss@escaping
.@escaping
Parameter in einer Protokollanforderung mit einem@escaping
Parameter in der Implementierung dieser Anforderung abgleichen (und umgekehrt für nicht maskierende Parameter). In Swift 2 war es dasselbe für@noescape
.Da @noescape die Standardeinstellung ist, gibt es zwei Möglichkeiten, um den Fehler zu beheben:
1) Wie @Hamish in seiner Antwort hervorhob, markieren Sie die Fertigstellung einfach als @escaping, wenn Sie sich für das Ergebnis interessieren und wirklich möchten, dass es entweicht (dies ist wahrscheinlich in @ Lukasz 'Frage mit Unit Tests als Beispiel und Möglichkeit der Asynchronisation der Fall Fertigstellung)
ODER
2) Behalten Sie das Standardverhalten von @noescape bei, indem Sie die Fertigstellung optional machen und die Ergebnisse insgesamt verwerfen, wenn Sie sich nicht um das Ergebnis kümmern. Zum Beispiel, wenn der Benutzer bereits "weggegangen" ist und der Controller der aufrufenden Ansicht nicht im Speicher hängen bleiben muss, nur weil ein unachtsamer Netzwerkanruf stattgefunden hat. Genau wie in meinem Fall, als ich hierher kam, um nach einer Antwort zu suchen, und der Beispielcode für mich nicht sehr relevant war, war das Markieren von @noescape nicht die beste Option, obwohl es auf den ersten Blick als das einzige klang.
quelle