Jak rozwiązać problem „Interpolacja ciągów tworzy opis debugowania dla wartości opcjonalnej; czy chciałeś wyrazić to wyraźnie? ” w Xcode 8.3 beta?

87

Od wersji beta 8.3 zilliony ostrzeżeń „Interpolacja łańcuchów generuje opis debugowania dla wartości opcjonalnej; czy chodziło Ci o to, aby było to jawne?” pojawił się w moim kodzie.

Na przykład ostrzeżenie pojawiło się w następującej sytuacji, w której opcje mogą prowadzić do zera:

let msg = "*** Error \(options["taskDescription"]): cannot load \(sUrl) \(error)"

Zgodnie z wcześniejszym projektem, dla mnie (i dla kompilatora) opcje opcjonalne były w porządku interpolowane jako „zero”. Ale kompilator zmienił zdanie.

Kompilator sugeruje dodanie konstruktora typu String z następującym opisem:

let msg = "*** Error \(String(describing: options["taskDescription"])): cannot load \(sUrl) \(error)"

Oczywiście wyniki są wyraźne, ale moim zdaniem również bardzo uciążliwe. Czy jest lepsza opcja? Czy muszę naprawić wszystkie te ostrzeżenia, czy lepiej poczekać na następną wersję beta?

Zrzut ekranu do opisu

Stéphane de Luca
źródło
26
Co za naprawdę irytujące ostrzeżenie ...
Jonny
Swift 3zepsułem własne logi popełniłem błąd, po prostu używając printzamiast tego. Powinieneś zawsze tworzyć własne opakowanie, w przeciwnym razie będziesz oszukiwany przez tego rodzaju „nową funkcję”.
superarts.org

Odpowiedzi:

105

Jest to zmiana wprowadzona w tym żądaniu ściągnięcia ze względu na fakt, że interpolacja Optional(...)do wynikowego ciągu jest często niepożądana i może być szczególnie zaskakująca w przypadkach z niejawnie rozpakowanymi opcjami . Pełną dyskusję na temat tej zmiany możesz zobaczyć na liście mailingowej tutaj .

Jak wspomniano w dyskusji o żądaniach ściągnięcia (chociaż niestety nie przez Xcode) - jednym nieco lepszym sposobem na wyciszenie ostrzeżenia niż użycie String(describing:)jest dodanie rzutowania do opcjonalnego typu tego, co interpolujesz, więc na przykład:

var i: Int? = 5
var d: Double? = nil

print("description of i: \(i as Int?)")    // description of i: Optional(5)
print("description of d: \(d as Double?)") // description of d: nil

Które można również uogólnić, aby as Optional:

print("description of i: \(i as Optional)") // description of i: Optional(5)
print("description of d: \(d as Optional)") // description of d: nil

W Swift 5, z nowym systemem interpolacji ciągów wprowadzonym przez SE-0228 , inną opcją jest dodanie niestandardowego appendInterpolationprzeciążenia dla DefaultStringInterpolation:

extension DefaultStringInterpolation {
  mutating func appendInterpolation<T>(optional: T?) {
    appendInterpolation(String(describing: optional))
  }
}

var i: Int? = 5
var d: Double? = nil

print("description of i: \(optional: i)") // description of i: Optional(5)
print("description of d: \(optional: d)") // description of d: nil

W razie potrzeby możesz nawet usunąć etykietę argumentu, aby całkowicie wyłączyć ostrzeżenie w module (lub w określonym pliku, jeśli oznaczysz go jako fileprivate):

extension DefaultStringInterpolation {
  mutating func appendInterpolation<T>(_ optional: T?) {
    appendInterpolation(String(describing: optional))
  }
}

var i: Int? = 5
var d: Double? = nil

print("description of i: \(i)") // description of i: Optional(5)
print("description of d: \(d)") // description of d: nil

Chociaż osobiście wolałbym zachować etykietę argumentu.

Hamish
źródło
Z propozycji nie wynika, czy ta zmiana będzie trwała? Co myślisz? @Hamish
Stéphane de Luca
@ StéphanedeLuca Odbyło się sporo dyskusji na liście mailingowej na temat innych rozwiązań, takich jak możliwość ?? "nil"wyciszenia ostrzeżenia, które wydawało się być dość popularne, więc może pojawić się w innej propozycji w najbliższej przyszłości. Zgadzam się, że to obejście jest mniej niż idealne - osobiście uważam, że raczej oczywiste jest oczekiwanie, Optional(...)że zostanie interpolowane do łańcucha dla silnego opcjonalnego - tak naprawdę tylko przypadek IUO wymagał tego ostrzeżenia IMO. Ale Swift stale się rozwija, więc to wszystko może się zmienić później. Ale na razie to właśnie mamy.
Hamish
Natknąłem się również na nieco „powiązany” problem w przypadku, jeśli nie pozwolę już na rozpakowywanie tutaj stackoverflow.com/questions/42543512/… jeśli możesz rzucić okiem? @Hamish
Stéphane de Luca
... w każdym razie ten kod to guard result == nil else { print("result was \(result as Optional)") return }
szaleństwo
1
@loretoparisi Dlaczego nie używać if let? tj if let result = result { print("result was \(result)"); return }. Nie wszystkie wczesne powroty muszą odbywać się ze strażnikami.
Hamish,
29

Dwa łatwiejsze sposoby rozwiązania tego problemu.

Opcja 1:

Pierwszym byłby „wymuszone rozpakowanie” wartości, którą chciałbyś zwrócić, używając huk (!)

var someValue: Int? = 5
print(someValue!)

Wynik:

5

Opcja 2:

Innym sposobem, który może być lepszym sposobem - jest „bezpieczne rozpakowanie” wartości, którą chcesz zwrócić.

var someValue: Int? = 5

if let newValue = someValue {
    print(newValue)
}

Wynik:

5

Poleciłbym wybrać opcję 2.

Wskazówka: unikaj rozwijania na siłę (!), Jeśli to możliwe, ponieważ nie jesteśmy pewni, czy zawsze będziemy mieli wartość do rozpakowania.

Mo Iisa
źródło
1
Jestem nowy, ale podoba mi się opcja 2, która pozwala sprawdzić opakowanie przed wydrukowaniem i zawsze masz możliwość wydrukowania czegoś innego, gdy jest rozpakowane
AbuTaareq
16

Wydaje się, że użycie ciągu znaków (opis: opcjonalne) jest najprostsze.

domyślna wartość ?? nie ma sensu dla nie-łańcuchów, np. Int.
Jeśli Int jest nil, to chcesz, aby dziennik pokazywał 'nil', a nie domyślnie inny Int, np. 0.

Trochę kodu placu zabaw do przetestowania:

var optionalString : String? = nil
var optionalInt : Int? = nil

var description_ = ""
description_ = description_ + "optionalString: \(String(describing: optionalString))\r"
description_ = description_ + "   optionalInt: \(String(describing: optionalInt))\r"

print(description_)

Wynik

optionalString: nil
optionalInt: nil
brian.clear
źródło
13

Po aktualizacji do Xcode 8.3 i otrzymaniu wielu komunikatów ostrzegawczych, wymyśliłem następujące, które bardziej przypominają oryginalne zachowanie wyjściowe, są łatwe do dodania, zmniejszają gadatliwość używania „String (opisujący :)” zarówno w kodzie, jak i na wyjściu .

Zasadniczo dodaj opcjonalne rozszerzenie, które daje ciąg opisujący rzecz w opcjonalnym lub po prostu „nil”, jeśli nie jest ustawione. Ponadto, jeśli rzecz opcjonalna jest Stringiem, umieść ją w cudzysłowie.

extension Optional {
    var orNil : String {
        if self == nil {
            return "nil"
        }
        if "\(Wrapped.self)" == "String" {
            return "\"\(self!)\""
        }
        return "\(self!)"
    }
}

I zastosowanie na placu zabaw:

var s : String?
var i : Int?
var d : Double?

var mixed = "s = \(s.orNil)    i = \(i.orNil)   d = \(d.orNil)" // "s = nil    i = nil   d = nil"

d = 3
i = 5
s = ""
mixed = "s = \(s.orNil)    i = \(i.orNil)   d = \(d.orNil)" // "s = ""    i = 5   d = 3.0"

s = "Test"
d = nil
mixed = "s = \(s.orNil)    i = \(i.orNil)   d = \(d.orNil)" // "s = "Test"    i = 5   d = nil"

Dzięki za pomoc z poniższego linku:

check-if-variable-is-an-optional-and-what-type-it-wrapaps

anorskdev
źródło
To rozwiązanie nie działa w opcjonalnym łańcuchu. Lubię a?.b?.c.orNil.
Vincent Sit
11

Zobacz poprawkę Ole Begemana . Kocham to. Tworzy ???operator, którego możesz następnie użyć w następujący sposób:

var someValue: Int? = 5
print("The value is \(someValue ??? "unknown")")
// → "The value is 5"
someValue = nil
print("The value is \(someValue ??? "unknown")")
// → "The value is unknown"
dar512
źródło
5
Odniesienie do swojego blogu opisującym ten byłby użyteczny myślę: oleb.net/blog/2016/12/optionals-string-interpolation
Nicolai Henriksen
8

Kliknij dwukrotnie żółty trójkąt wyświetlany w wierszu zawierającym to ostrzeżenie. Spowoduje to wyświetlenie FixIt z dwoma rozwiązaniami.

Dodano zrzut ekranu

  1. Użyj, String(describing:)aby wyciszyć to ostrzeżenie:

    Używając tego, stanie się String(describing:<Variable>)

    Na przykład. :String(describing: employeeName)

  2. Podaj, default valueaby uniknąć tego ostrzeżenia:

    Używając tego, stanie się (<Variable> ?? default value)

    Na przykład.: employeeName ?? “Anonymous” as! String

Jayprakash Dubey
źródło
1
Tak,
wybrałbym
1
Świetna odpowiedź! Koalescencja zerowa działa dobrze z tym, jeśli masz alternatywną wartość ciągu do podania
Lance Samaria,
1

Szybki 5

Moje rozwiązanie polega na utworzeniu obiektu, do extensionktórego należy rozpakować .OptionalAny

Kiedy logujesz obiekt lub drukujesz go, możesz zobaczyć rzeczywiste objector <nil>⭕️(połączenie tekstu i znaku wizualnego). Warto się temu przyjrzeć, zwłaszcza w dzienniku konsoli.

extension Optional {
    var logable: Any {
        switch self {
        case .none:
            return "<nil>|⭕️"
        case let .some(value):
            return value
        }
    }
}

// sample
var x: Int?
print("Logging optional without warning: \(x.logable)")
// → Logging optional without warning: <nil>|⭕️
nahung89
źródło
0

Utwórz metodę interpolacji, która akceptuje opcjonalny typ ogólny z nienazwanym parametrem. Wszystkie twoje irytujące ostrzeżenia magicznie znikną.

extension DefaultStringInterpolation {
  mutating func appendInterpolation<T>(_ optional: T?) {
    appendInterpolation(String(describing: optional))
  }
}
ScottyBlades
źródło