Xcode 8 / Swift 3: „Wyrażenie typu UIViewController? jest nieużywany ”

230

Mam następującą funkcję, która skompilowała się wcześniej, ale generuje ostrzeżenie za pomocą Xcode 8.

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

„Wyrażenie typu„ UIViewController? ”Nie jest używane”.

Dlaczego to mówi i czy można to usunąć?

Kod wykonuje się zgodnie z oczekiwaniami.

Gruntcakes
źródło

Odpowiedzi:

498

TL; DR

popViewController(animated:)zwraca UIViewController?, a kompilator wyświetla to ostrzeżenie, ponieważ nie przechwytujesz wartości. Rozwiązaniem jest przypisanie go do podkreślenia:

_ = navigationController?.popViewController(animated: true)

Swift 3 Change

Przed wersją Swift 3 wszystkie metody miały domyślnie „wynik do odrzucenia”. Ostrzeżenie nie pojawi się, jeśli nie uchwycisz, co zwróciła metoda.

Aby powiedzieć kompilatorowi, że wynik powinien zostać przechwycony, trzeba było dodać @warn_unused_resultprzed deklaracją metody. Byłby używany do metod, które mają zmienną postać (np. sortI sortInPlace). Dodałbyś, @warn_unused_result(mutable_variant="mutableMethodHere")żeby poinformować o tym kompilator.

Jednak w Swift 3 zachowanie jest odwrócone. Wszystkie metody ostrzegają teraz, że zwracana wartość nie jest przechwytywana. Jeśli chcesz powiedzieć kompilatorowi, że ostrzeżenie nie jest konieczne, dodajesz @discardableResultprzed deklaracją metody.

Jeśli nie chcesz używać zwracanej wartości, musisz jawnie powiedzieć kompilatorowi, przypisując ją do znaku podkreślenia:

_ = someMethodThatReturnsSomething()

Motywacja do dodania tego do Swift 3:

  • Zapobieganie możliwym błędom (np. Używanie sortmyślenia modyfikuje kolekcję)
  • Wyraźny zamiar nie przechwytywania lub konieczności przechwytywania wyniku dla innych współpracowników

Interfejs API UIKit wydaje się być opóźniony, nie dodając @discardableResultdo całkowicie normalnego (jeśli nie bardziej powszechnego) użycia popViewController(animated:)bez przechwytywania wartości zwracanej.

Czytaj więcej

tktsubota
źródło
15
To jest (moim zdaniem) zdecydowanie krok wstecz od Swift 2, zwłaszcza gdy istnieją metody takie jak to, że mimo, że zrobić zwrócić wartość, są w pełni aktualne przypadki użycia, gdzie po prostu nie używaj go.
Nicolas Miari
15
1. Nie potrzebujesz let: możesz po prostu przypisać do _ bez poprzedzania go za pomocą letlub var.
rickster
1
@rickster Nie wiedziałem, że doda do odpowiedzi.
tktsubota,
5
2. @NicolasMiari Zgłoś błąd . Istnieje adnotacja ( @discardableResult) dla funkcji, które zwracają wartość, ale tam, gdzie oczekuje się, że można zignorować zwracaną wartość. UIKit po prostu nie zastosował tej adnotacji do swojego interfejsu API.
rickster
37
To okropna składnia. Dlaczego mieliby to zrobić? Fuj
David S.
38

Kiedy życie daje ci cytryny, zrób przedłużenie:

import UIKit

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

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

Pamiętaj, że dodanie czegoś takiego @discardableResult func pop(animated: Bool) -> UIViewController?spowoduje ostrzeżenie, którego próbujesz uniknąć.

Dzięki rozszerzeniu możesz teraz pisać:

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

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

Edycja: Dodano także popToRoot.

CodeReaper
źródło
To powinno być przyjęte rozwiązanie, ponieważ jest to najczystsze rozwiązanie tego, co z pewnością zostanie naprawione w aktualizacji Xcode.
Philip Broadway
24

W Swift 3 ignorowanie wartości zwracanej przez funkcję, która ma zadeklarowaną wartość zwrotną, powoduje wyświetlenie ostrzeżenia.

Jednym ze sposobów rezygnacji z tego jest oznaczenie funkcji @discardableResultatrybutem. Ponieważ nie masz kontroli nad tą funkcją, to nie zadziała.

Inną metodą pozbycia się ostrzeżenia jest przypisanie wartości _. To informuje kompilator, że wiesz, że metoda zwraca wartość, ale nie chcesz zachować jej w pamięci.

let _ = navigationController?.popViewController(animated: true)
Matthew Seaman
źródło
2
Myślę, że będziemy musieli trzymać się _brzydoty, dopóki Apple nie zaktualizuje UIKit tym nowym atrybutem.
Nicolas Miari,
2
Niestety @discardableResultnie działa (przynajmniej nadal krakuje przy 8b4). Friedrich Schiller uwielbiał zgniłe jabłka. Prawdopodobnie kwestia gustu :-(
qwerty_so
5

Zrzut ekranu 1

Chociaż to, work correctly if kept as it isalenumber of warning increases.

Rozwiązaniem jest po prostu, replace it with underscore ( _ )choć wydaje się brzydkie.

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

Zrzut ekranu 2

Jayprakash Dubey
źródło
2

W tej sytuacji użyj discardableResult .

Zgodnie z <Swift Programming Language>, rozdział Language Reference - Atrybuty.

discardableResult

Zastosuj ten atrybut do deklaracji funkcji lub metody, aby ukryć ostrzeżenie kompilatora, gdy funkcja lub metoda zwracająca wartość zostanie wywołana bez użycia jej wyniku.

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

Demo znajduje się również w <Swift Programming Language>, rozdział Language Guide - Methods.

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

Ponieważ niekoniecznie jest to błąd w kodzie, który wywołuje metodę forward (to :) w celu zignorowania wartości zwracanej, funkcja ta jest oznaczona atrybutem @discardableResult. Aby uzyskać więcej informacji o tym atrybucie, zobacz Atrybuty.

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

czarna Perła
źródło
0

Jeśli chcesz iść drogą rozszerzeń takich jak odpowiedź CodeReaper, powinieneś użyć @descardableResult. Zachowuje to wszystkie możliwości, ale wycisza ostrzeżenie.

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
źródło
-1

Innym sposobem jest rozpakowanie self.navigationController?wartości i wywołanie popViewControllerfunkcji.

    if let navigationController = navigationController {
        navigationController.popViewController(animated: true)
    }
muazhud
źródło