2014-09-16 13 views
5

Mam niestandardowy obiekt o nazwie Field. Zasadniczo używam go do definiowania pojedynczego pola w formularzu.Swift, jeśli pozwala ocenić pomyślnie na Opcjonalne (zero)

class Field { 

    var name: String 
    var value: Any? 

    // initializers here... 
} 

Gdy użytkownik wysyła formularz, sprawdzić poprawność każdego z Field obiektów, aby upewnić się, że zawierają prawidłowe wartości. Niektóre pola nie są wymagane, więc czasami celowo nil do nieruchomości value tak:

field.value = nil 

To wydaje się stwarzać problemy podczas korzystania z if-let, aby określić, czy pole jest zerowa lub nie.

if let value = field.value { 
    // The field has a value, ignore it... 
} else { 
    // Add field.name to the missing fields array. Later, show the 
    // missing fields in a dialog. 
} 

ustawić punkty przerwania w powyższym if-else i gdy field.value został celowo ustawiony na zero, to przechodzi przez blok if-let, a nie inny. Jednak dla pól, których field.value zostawiłem niezainicjowany i nieprzypisany, program przechodzi do bloku else.

Próbowałem drukowanie field.value i value wewnątrz bloku if-let:

if let value = field.value { 
    NSLog("field.value: \(field.value), value: \(value)") 
} 

I to jest to, co mam:

field.value: opcjonalnie (zero), wartość: zero

Więc pomyślałem, że może z opcjami, jedna rzecz jest niezainicjowana, a inna mieć wartość e z zera. Ale nawet dodanie innego, jeśli w środku if-let nie sprawi, że kompilator będzie szczęśliwy:

if let value = field.value { 
    if value == nil { // Cannot invoke '==' with an argument list of type '(Any, NilLiteralConvertible)' 
    } 
} 

Jak sobie z tym poradzić? Chcę tylko sprawdzić, czy field.value jest nil.

+1

Nie można odtworzyć. Którą wersję Xcode używasz? –

+0

Xcode 6 GM. Próbowałem go ponownie uruchomić, wciąż nie ma szczęścia. :( –

+0

Czy możesz opublikować kompletny samodzielny przykład demonstrujący problem? –

Odpowiedz

3

Wierzę, że to dlatego, że Any? pozwala na dowolną wartość, a Optional.None jest interpretowane jako kolejna wartość, ponieważ Optional jest wyliczeniem!

AnyObject? powinien być w stanie to zrobić, ponieważ tylko może zawierać Optional.Some([any class object]), które nie dopuszczają przypadek Optional.Some(Optional) z wartością Optional.None.

To jest bardzo mylące, o którym można nawet mówić. Chodzi o to: spróbuj AnyObject? zamiast Any? i sprawdź, czy to działa.


Więcej do punktu, jedna z komentarzem Matt wspomina, że ​​powodem, dla którego chce użyć Any jest wyborem, który może być albo pole do wprowadzania tekstu lub pole przeznaczone do wyboru obiektu danych Core.

Sprawą Swifty w tym przypadku jest użycie enum with associated values, w zasadzie to samo co tagged/discriminated union.Oto jak deklarują, przydzielać i używać takiego ENUM:

enum DataSelection { 
    case CoreDataObject(NSManagedObject) 
    case StringField(String) 
} 

var selection : DataSelection? 

selection = .CoreDataObject(someManagedObject) 

if let sel = selection { // if there's a selection at all 
    switch sel { 
     case .CoreDataObject(let coreDataObj): 
      // do what you will with coreDataObj 
     case .StringField(let string): 
      // do what you will with string 
    } 
} 

Korzystanie enum takich jak to, nie ma potrzeby martwić się o rzeczy, które mogłyby być ukrywanie wewnątrz tego Any?. Istnieją dwa przypadki i są one udokumentowane. Oczywiście zmienna wyboru może być opcjonalna bez żadnych zmartwień.

+1

Poprawną odpowiedzią jest" uniknij 'Any" at wszystkie koszty ". Jeśli używasz opcjonalnego 'Any', jesteś wkręcony. – Sulthan

+2

Tak, ponieważ nie wydaje się, że istnieje sposób, aby język mógł rozróżnić między opcją "Opcjonalne.None" przeznaczoną do użycia jako wartość rzeczywista lub oznaczeniem braku wartości w opcjach "Dowolny". – Jesper

0

Istnieje wskazówka, aby zastąpić mój typ Any? wyliczeniem, ale nie mogłem usunąć tego błędu z mojej głowy. Zmiana mojego podejścia nie zmienia faktu, że coś jest nie tak z moim bieżącym i musiałem dowiedzieć się, jak dotarłem do wyjścia Optional(nil).

Udało mi się odtworzyć błąd, pisząc poniższy kontroler widoku w nowym widoku pojedynczym. Zauważ podpis init.

import UIKit 

class Field { 
    var name: String = "Name" 
    var value: Any? 

    init(_ name: String, _ value: Any) { 
     self.name = name 
     self.value = value 
    } 
} 

class AppState { 
    var currentValue: Field? 
} 

class ViewController: UIViewController { 
    override func viewDidLoad() { 
     super.viewDidLoad() 

     let f = Field("Amount", AppState().currentValue) 
     NSLog("\(f.value)") 

    } 
} 

W skrócie, przechodził do wartości zerowej (AppState().currentValue) do inicjatora, który przyjmuje Any i przypisuje go do właściwości, którego typ jest Any?. Najśmieszniejsze jest tutaj, gdybym zamiast bezpośrednio przekazywane nil, kompilator będzie narzekać:

let f = Field("Amount", nil) // Type 'Any' does not conform to protocol 'NilLiteralConvertible' 

Wydaje się, że gdzieś po drodze, Swift owija wartość nil z AppState().currentValue w opcjonalną, stąd Optional(nil).

+0

'Nil' nie istnieje jako wartość (tylko jako słowo kluczowe), ale' Opcjonalne.None' jest wartością domyślną dowolnej zmiennej opcjonalnej i wypisuje 'nil', gdy zostanie poproszony o opisanie siebie. – Jesper