swift swift3 closures xcode8-beta6

swift - El uso cerrado del parámetro de no escape puede permitirle escapar



swift3 closures (2)

Como @noescape es el predeterminado, hay 2 opciones para corregir el error:

1) como señaló @Hamish en su respuesta, simplemente 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)

func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)

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 "retirado" 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.

func fetchData(location: String, completion: ((DataFetchResult) -> Void)?) { ... completion?(self.result) }

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?


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 sobrevivirán 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 :

protocol DataServiceType { func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void) func cachedData(location: String) -> Data? }

func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void) { // ... }

Consulte la propuesta de Swift Evolution para obtener más información sobre este cambio.