Xcode 8 / Swift 3: “Expresión del tipo UIViewController? está sin usar "advertencia

230

Tengo la siguiente función que se compiló limpiamente anteriormente pero genera una advertencia con Xcode 8.

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

"La expresión de tipo" UIViewController? "No está en uso".

¿Por qué dice esto y hay una manera de eliminarlo?

El código se ejecuta como se esperaba.

Tortas Grunt
fuente

Respuestas:

498

TL; DR

popViewController(animated:)vuelve UIViewController?, y el compilador está dando esa advertencia ya que no está capturando el valor. La solución es asignarlo a un guión bajo:

_ = navigationController?.popViewController(animated: true)

Cambio rápido 3

Antes de Swift 3, todos los métodos tenían un "resultado descartable" por defecto. No se produciría ninguna advertencia cuando no capturara lo que devolvió el método.

Para decirle al compilador que se debe capturar el resultado, debe agregarlo @warn_unused_resultantes de la declaración del método. Se usaría para métodos que tienen una forma mutable (ej. sortY sortInPlace). Agregarías @warn_unused_result(mutable_variant="mutableMethodHere")para contarle al compilador.

Sin embargo, con Swift 3, el comportamiento se voltea. Todos los métodos ahora advierten que el valor de retorno no se captura. Si desea decirle al compilador que la advertencia no es necesaria, agregue @discardableResultantes de la declaración del método.

Si no desea utilizar el valor de retorno, debe decirle explícitamente al compilador asignándolo a un guión bajo:

_ = someMethodThatReturnsSomething()

Motivación para agregar esto a Swift 3:

  • Prevención de posibles errores (por ejemplo, usando sortpensar que modifica la colección)
  • Intento explícito de no capturar o necesitar capturar el resultado para otros colaboradores

La API de UIKit parece estar retrasada en esto, no agregando @discardableResultpara el uso perfectamente normal (si no más común) popViewController(animated:)sin capturar el valor de retorno.

Lee mas

tktsubota
fuente
15
Esto es (en mi opinión) definitivamente un paso atrás de Swift 2, especialmente cuando existen métodos como este que, a pesar de que hacen devolver un valor, hay casos de uso perfectamente válidas en el que sólo no lo uso.
Nicolas Miari
15
1. No necesita el let: solo puede asignar a _ sin precederlo con leto var.
rickster
1
@rickster No sabía que se agregará a la respuesta.
tktsubota
55
2. @NicolasMiari Archiva un error . Hay una anotación ( @discardableResult) para funciones que devuelven un valor pero donde se espera que uno pueda ignorar el valor devuelto. UIKit simplemente no ha aplicado esa anotación a su API.
rickster
37
Esta es una sintaxis horrible. ¿Por qué harían esto? Yuck
David S.
38

Cuando la vida te dé limones, haz una extensión:

import UIKit

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

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

Tenga en cuenta que agregar algo como esto @discardableResult func pop(animated: Bool) -> UIViewController?dará como resultado la misma advertencia que está tratando de evitar.

Con la extensión ahora puedes escribir:

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

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

Editar: También se agregó popToRoot.

CodeReaper
fuente
Esta debería ser la solución aceptada, ya que es la solución más limpia para lo que seguramente se solucionará en una actualización de Xcode.
Philip Broadway
24

En Swift 3, ignorar el valor de retorno de una función que tiene un valor de retorno declarado da como resultado una advertencia.

Una forma de optar por esto es marcar la función con el @discardableResultatributo. Como no tienes control sobre esta función, eso no funcionará.

El otro método para deshacerse de la advertencia es asignar el valor a _. Esto le dice al compilador que sabe que el método devuelve un valor pero no desea retenerlo en la memoria.

let _ = navigationController?.popViewController(animated: true)
Matthew Seaman
fuente
2
Supongo que tendremos que seguir con lo feo _hasta que Apple actualice UIKit con este nuevo atributo.
Nicolas Miari
2
Lamentablemente @discardableResultno funciona (al menos todavía se rompe con 8b4). Friedrich Schiller amaba las manzanas podridas. Probablemente sea una cuestión de gustos :-(
qwerty_so
5

Captura de pantalla 1

Aunque solo sea work correctly if kept as it iselnumber of warning increases.

La solución es simplemente replace it with underscore ( _ )aunque parezca feo.

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

Captura de pantalla 2

Jayprakash Dubey
fuente
2

Utilice discardableResult en esta condición.

De acuerdo con <Swift Programming Language>, capítulo Referencia del lenguaje - Atributos.

resultado descartable

Aplique este atributo a una declaración de función o método para suprimir la advertencia del compilador cuando se llama a la función o método que devuelve un valor sin usar su resultado.

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

También hay una demostración en <Swift Programming Language>, capítulo Guía del lenguaje - Métodos.

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

Debido a que no es necesariamente un error para el código que llama al método advance (to :) para ignorar el valor de retorno, esta función está marcada con el atributo @discardableResult. Para obtener más información sobre este atributo, vea Atributos.

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

Perla Negra
fuente
0

Si desea seguir el camino de las extensiones como la respuesta de CodeReaper, debe usar @descardableResult. Esto mantiene todas las posibilidades, pero silencia la advertencia.

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
fuente
-1

Otra forma es que puede desenvolver el self.navigationController?valor y llamar a la popViewControllerfunción.

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