Parâmetro de fechamento de escape opcional rápido

162

Dado:

typealias Action = () -> ()

var action: Action = { }

func doStuff(stuff: String, completion: @escaping Action) {
    print(stuff)
    action = completion
    completion()
}

func doStuffAgain() {
    print("again")
    action()
}

doStuff(stuff: "do stuff") { 
    print("swift 3!")
}

doStuffAgain()

Existe alguma maneira de fazer o completionparâmetro (e action) do tipo Action?e também manter @escaping?

Alterar o tipo gera o seguinte erro:

O atributo @escaping se aplica apenas a tipos de função

Removendo o @escapingatributo, o código compila e executa, mas não parece estar correto, pois o completionfechamento está escapando do escopo da função.

Lescai Ionel
fonte
21
"Removendo o @escapingatributo, o código compila e executa" - Isso porque, conforme descrito no SR-2444 , Action?está, por padrão, escapando. Portanto, remover @escapingao usar o fechamento opcional realiza o que você precisa.
Rob
Tipo fechamentos de alias estão escapando
Masih
Aqui está um excelente artigo de Ole Begemann que descreve por que está acontecendo e algumas soluções alternativas, se você deseja que os parâmetros opcionais sejam @noescape.
Senseful

Respostas:

122

Há um relatório SR-2552 que @escapingnão reconhece o alias do tipo de função. é por isso que o erro @escaping attribute only applies to function types. você pode solucionar esse problema expandindo o tipo de função na assinatura da função:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: (@escaping ()->())?) {
    print(stuff)
    action = completion
    completion?()
}

func doStuffAgain() {
    print("again")
    action?()
}

doStuff(stuff: "do stuff") {
    print("swift 3!")
}

doStuffAgain()

EDIT 1 :

Na verdade, eu estava na versão beta do xcode 8, onde o bug SR-2552 ainda não havia sido resolvido. corrigindo esse erro, introduziu um novo (o que você está enfrentando) que ainda está aberto. veja SR-2444 .

A solução alternativa @Michael Ilseman apontada como uma solução temporária é remover o @escapingatributo do tipo de função opcional, que mantém a função como escapada .

func doStuff(stuff: String, completion: Action?) {...}

EDIT 2 :

O SR-2444 foi fechado informando explicitamente que os fechamentos nas posições dos parâmetros não estão escapando e precisa que eles sejam marcados com @escapingpara fazê-los escapar, mas os parâmetros opcionais estão implicitamente escapando, já que ((Int)->())?é um sinônimo de Optional<(Int)->()>, os fechamentos opcionais estão escapando.

Jans
fonte
5
Começando agora@escaping may only be applied to parameters of function type func doStuff(stuff: String, completion: (@escaping ()->())?) {
Lescai Ionel
1
a temporary solution is remove the @escaping attribute from optional function type, that keep the function as escaping. Você pode explicar isso mais? A semântica padrão no swift 3 é sem escape. Embora seja compilado sem @ escape, receio que causará problemas ao ser tratado como não escapante. Isso não é verdade?
Pat Niemeyer
49
Depois de ler mais, vejo que o SR-2444 diz que todos os fechamentos opcionais são tratados como escapes, o que é um bug complementar :) Vou assumir que, quando for corrigido, a compilação nos avisará para fazer a alteração.
Pat Niemeyer
Talvez um pouco fora de tópico; mas como isso funciona @autoclosure? É possível obter o mesmo erro ...
Gee.E
está funcionando sem @escaping __ func doStuff (material: String, conclusão: (() -> ())?) {
Феннур Мезитов
226

de: lista de discussão swift-users

Basicamente, @escaping é válido apenas em fechamentos na posição de parâmetro de função. A regra noescape por padrão se aplica apenas a esses fechamentos na posição do parâmetro de função, caso contrário, eles estão escapando. Agregados, como enumerações com valores associados (por exemplo, opcional), tuplas, estruturas, etc., se tiverem fechamentos, siga as regras padrão para fechamentos que não estão na posição do parâmetro de função, ou seja, estão escapando.

Portanto, o parâmetro de função opcional é @escaping por padrão.
O @noeascape se aplica apenas ao parâmetro function por padrão.

Dmitry Coolerov
fonte
7
Eu acho que isso adiciona informações mais importantes ao assunto, deve ser uma resposta aceita.
22617 Damian Dudycz
A resposta fundamentada. Quão provável é que isso possa mudar?
GoldenJoe
2
Isso faz sentido, já que dizer tecnicamente (()->Void)?é o mesmo que dizer que você possui Optional<()->Void>e, para Optionalmanter a propriedade, ele deve aceitar apenas @escapingfunções. Terceiro, essa deve ser a resposta aceita. Obrigado.
Dean Kelly
22

Eu tive um problema semelhante porque misturar @escapinge não @escapingé muito confuso, especialmente se você precisar passar pelos fechamentos.

Acabei atribuindo um valor padrão no-op ao parâmetro de fechamento via = { _ in }, o que acho que faz mais sentido:

func doStuff(stuff: String = "do stuff",
        completion: @escaping (_ some: String) -> Void = { _ in }) {
     completion(stuff)
}

doStuff(stuff: "bla") {
    stuff in
    print(stuff)
}

doStuff() {
    stuff in
    print(stuff)
}
Freeman Man
fonte
Isso é limpo e bonito na implementação.
Fred Faust
2
E se eu quiser verificar se esse bloco está vazio / vazio?
Vyachaslav Gerchicov
2
Que chatice, isso não funciona para um método de protocolo. "Argumento padrão não permitido em um método de protocolo" (Xcode 8.3.2).
Mike Taverne
17

Eu consegui trabalhar no Swift 3 sem nenhum aviso apenas desta maneira:

func doStuff(stuff: String, completion: (()->())? ) {
    print(stuff)
    action = completion
    completion?()
}
Igor
fonte
4

O importante a entender no exemplo é que, se você mudar Actionpara Action?o fechamento, está escapando. Então, vamos fazer o que você propõe:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: Action?) {
    print(stuff)
    action = completion
    completion?()
}

Ok, agora vamos ligar doStuff:

class ViewController: UIViewController {
    var prop = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        doStuff(stuff: "do stuff") {
            print("swift 3!")
            print(prop) // error: Reference to property 'prop' in closure 
                        // requires explicit 'self.' to make capture semantics explicit
        }
    }
}

Bem, esse requisito só surge para escapar de fechamentos. Então o fechamento está escapando. É por isso que você não marca como escapando - já está escapando.

mate
fonte