Tengo un protocolo:
enum DataFetchResult {
case success(data: Data)
case failure
}
protocol DataServiceType {
func fetchData(location: String, completion: (DataFetchResult) -> (Void))
func cachedData(location: String) -> Data?
}
Con un ejemplo de implementación:
/// 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)
}
}
}
El código anterior compiló y funcionó en Swift3 (Xcode8-beta5) pero ya no funciona con beta 6. ¿Me puede señalar la causa subyacente?
swift
swift3
closures
xcode8-beta6
Lukasz
fuente
fuente
Respuestas:
Esto se debe a un cambio en el comportamiento predeterminado para los parámetros del tipo de función. Antes de Swift 3 (específicamente la compilación que se envía con Xcode 8 beta 6), por defecto estarían escapando; tendría que marcarlos
@noescape
para evitar que se almacenen o capturen, lo que garantiza que no durarán más que la duración de la llamada a la función.Sin embargo, ahora
@noescape
es el valor predeterminado para los parámetros de tipo función. Si desea almacenar o capturar tales funciones, ahora debe marcarlas@escaping
:Consulte la propuesta de Swift Evolution para obtener más información sobre este cambio.
fuente
async
parámetro de función (y, por lo tanto, lacompletion
función) sefetchData
invoque antes de las salidas, y por lo tanto debe ser así@escaping
.@escaping
parámetro en un requisito de protocolo con un@escaping
parámetro en la implementación de ese requisito (y viceversa para los parámetros que no escapan). Fue lo mismo en Swift 2 para@noescape
.Como @noescape es el predeterminado, hay 2 opciones para corregir el error:
1) como señaló @Hamish en su respuesta, marque la finalización como @escaping si le importa el resultado y realmente quiere que se escape (ese es probablemente el caso en la pregunta de @ Lukasz con las Pruebas de Unidad como ejemplo y posibilidad de sincronización) terminación)
O
2) mantenga el comportamiento predeterminado de @noescape haciendo que la finalización sea opcional descartando los resultados por completo en los casos en que no le importe el resultado. Por ejemplo, cuando el usuario ya se ha "alejado" y el controlador de vista de llamada no tiene que quedarse en la memoria solo porque hubo una llamada de red descuidada. Tal como sucedió en mi caso cuando vine aquí en busca de respuesta y el código de muestra no era muy relevante para mí, por lo que marcar @noescape no fue la mejor opción, evento aunque sonó como el único a primera vista.
fuente