Xcode 8 / Swift 3: “Expressão do tipo UIViewController? não utilizado "aviso

230

Eu tenho a seguinte função que compilou de forma limpa anteriormente, mas gera um aviso com o Xcode 8.

func exitViewController()
{
    navigationController?.popViewController(animated: true)
}

"Expressão do tipo" UIViewController? "Não está sendo usada".

Por que está dizendo isso e existe uma maneira de removê-lo?

O código é executado conforme o esperado.

Gruntcakes
fonte

Respostas:

498

TL; DR

popViewController(animated:)retorna UIViewController?e o compilador está emitindo esse aviso, pois você não está capturando o valor. A solução é atribuí-lo a um sublinhado:

_ = navigationController?.popViewController(animated: true)

Mudança rápida 3

Antes do Swift 3, todos os métodos tinham um "resultado descartável" por padrão. Nenhum aviso ocorreria quando você não capturasse o que o método retornou.

A fim de dizer ao compilador que o resultado deve ser capturado, você teve que adicionar @warn_unused_resultantes da declaração de método. Seria usado para métodos que têm uma forma mutável (ex. sortE sortInPlace). Você adicionaria @warn_unused_result(mutable_variant="mutableMethodHere")para informar ao compilador.

No entanto, com o Swift 3, o comportamento é invertido. Todos os métodos agora alertam que o valor de retorno não é capturado. Se você quiser informar ao compilador que o aviso não é necessário, adicione-o @discardableResultantes da declaração do método.

Se você não quiser usar o valor de retorno, precisará informar explicitamente o compilador atribuindo-o a um sublinhado:

_ = someMethodThatReturnsSomething()

Motivação para adicionar isso ao Swift 3:

  • Prevenção de possíveis erros (por exemplo, usando o sortpensamento de modificar a coleção)
  • Intenção explícita de não capturar ou precisar capturar o resultado para outros colaboradores

A API do UIKit parece estar atrasada nisso, não adicionando @discardableResultpara o uso perfeitamente normal (se não mais comum) popViewController(animated:)sem capturar o valor de retorno.

consulte Mais informação

tktsubota
fonte
15
Este é (na minha opinião) definitivamente um passo para trás de Swift 2, especialmente quando existem métodos como este que, apesar de fazer retornar um valor, há casos de uso perfeitamente válidas, onde você simplesmente não usá-lo.
Nicolas Miari
15
1. Você não precisa de let: você pode simplesmente atribuir a _ sem precedê-lo com letou var.
Rickster
1
@ Rickster Não sabia que isso irá adicionar à resposta.
tktsubota
5
2. @NicolasMiari Arquive um bug . Há uma anotação ( @discardableResult) para funções que retornam um valor, mas onde é esperado que alguém possa ignorar o valor de retorno. O UIKit simplesmente não aplicou essa anotação à API.
Rickster
37
Essa é uma sintaxe horrível. Por que eles fariam isso? Que nojo.
David S.
38

Quando a vida lhe der limões, faça uma extensão:

import UIKit

extension UINavigationController {
    func pop(animated: Bool) {
        _ = self.popViewController(animated: animated)
    }

    func popToRoot(animated: Bool) {
        _ = self.popToRootViewController(animated: animated)
    }
}

Observe que adicionar algo como @discardableResult func pop(animated: Bool) -> UIViewController?resultará no mesmo aviso que você está tentando evitar.

Com a extensão, agora você pode escrever:

func exitViewController()
{
    navigationController?.pop(animated: true)
}

func popToTheRootOfNav() {
    navigationController?.popToRoot(animated: true)
}

Edit: Adicionado popToRoot também.

CodeReaper
fonte
Essa deve ser a solução aceita, pois é a correção mais limpa para o que certamente será corrigido em uma atualização do Xcode.
Philip Broadway
24

No Swift 3, ignorar o valor de retorno de uma função que possui um valor de retorno declarado resulta em um aviso.

Uma maneira de desativar isso é marcar a função com o @discardableResultatributo Como você não tem controle sobre essa função, isso não funcionará.

O outro método para se livrar do aviso é atribuir o valor a _. Isso informa ao compilador que você sabe que o método retorna um valor, mas não deseja mantê-lo na memória.

let _ = navigationController?.popViewController(animated: true)
Matthew Seaman
fonte
2
Acho que teremos que ficar com o feio _até a Apple atualizar o UIKit com esse novo atributo.
Nicolas Miari
2
Infelizmente @discardableResult, não funciona (pelo menos ainda resmunga com 8b4). Friedrich Schiller adorava maçãs podres. Provavelmente uma questão de gosto :-(
qwerty_so
5

Captura de tela 1

Embora, work correctly if kept as it ismas onumber of warning increases.

A solução é simplesmente replace it with underscore ( _ )parecer feia.

Eg.  _ = navigationController?.popViewController(animated: true)

Captura de tela 2

Jayprakash Dubey
fonte
2

Use discardableResult nesta condição.

De acordo com <Swift Programming Language>, capítulo Reference Language - Attributes.

discardableResult

Aplique esse atributo a uma declaração de função ou método para suprimir o aviso do compilador quando a função ou método que retorna um valor for chamado sem usar seu resultado.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-ID347

Há também uma demonstração em <Swift Programming Language>, capítulo Guia de idiomas - Métodos.

@discardableResult
    mutating func advance(to level: Int) -> Bool {
    ...
return true
}

Como não é necessariamente um erro o código que chama o método advance (to :) para ignorar o valor retornado, essa função é marcada com o atributo @discardableResult. Para mais informações sobre este atributo, consulte Atributos.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Methods.html#//apple_ref/doc/uid/TP40014097-CH15-ID234

Pérola Negra
fonte
0

Se você quiser seguir o caminho de extensões como a resposta do CodeReaper, use @descardableResult. Isso mantém todas as possibilidades, mas silencia o aviso.

import UIKit

extension UINavigationController {
    @discardableResult func pop(animated: Bool) -> UIViewController? {
        return self.popViewController(animated: animated)
    }

    @discardableResult func popToRoot(animated: Bool) -> [UIViewController]? {
        return self.popToRootViewController(animated: animated)
    }
}
Casper Zandbergen
fonte
-1

Outra maneira é você pode desembrulhar o self.navigationController?valor e chamar a popViewControllerfunção.

    if let navigationController = navigationController {
        navigationController.popViewController(animated: true)
    }
muazhud
fonte