Eu tenho um protocolo:
enum DataFetchResult {
case success(data: Data)
case failure
}
protocol DataServiceType {
func fetchData(location: String, completion: (DataFetchResult) -> (Void))
func cachedData(location: String) -> Data?
}
Com um exemplo de implementação:
/// 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)
}
}
}
O código acima compilou e funcionou no Swift3 (Xcode8-beta5), mas não funciona mais com o beta 6. Você pode me indicar a causa subjacente?
swift
swift3
closures
xcode8-beta6
Lukasz
fonte
fonte
Respostas:
Isso ocorre devido a uma alteração no comportamento padrão dos parâmetros do tipo de função. Antes do Swift 3 (especificamente a compilação que acompanha o Xcode 8 beta 6), eles teriam como padrão escapar - você teria que marcá-los
@noescape
para impedir que eles fossem armazenados ou capturados, o que garante que eles não sobreviverão à duração da chamada de função.No entanto, agora
@noescape
é o padrão para parâmetros do tipo de função. Se você deseja armazenar ou capturar essas funções, agora precisa marcá-las@escaping
:Consulte a proposta do Swift Evolution para obter mais informações sobre essa alteração.
fonte
async
parâmetro da função (e, portanto, acompletion
função) seja chamado antes dasfetchData
saídas - e, portanto, deve ser@escaping
.@escaping
parâmetro em um requisito de protocolo com um@escaping
parâmetro na implementação desse requisito (e vice-versa para parâmetros sem escape). Foi o mesmo em Swift 2 para@noescape
.Como @noescape é o padrão, existem 2 opções para corrigir o erro:
1) como @Hamish apontou em sua resposta, marque a conclusão como @escaping se você se importa com o resultado e realmente quer que ele escape (esse provavelmente é o caso na pergunta de @ Lukasz com testes de unidade como exemplo e possibilidade de assíncrona conclusão)
OU
2) mantenha o comportamento padrão do @noescape, tornando opcional a conclusão descartando os resultados completamente nos casos em que você não se importa com o resultado. Por exemplo, quando o usuário já "se afastou" e o controlador de exibição de chamada não precisa travar na memória apenas porque houve uma chamada de rede descuidada. Assim como no meu caso, quando cheguei aqui, procurando por respostas e o código de exemplo não era muito relevante para mim, marcar o @noescape não era a melhor opção, apesar de soar como o único à primeira vista.
fonte