2014-09-02 10 views
6

Robię to, co uważam za bardzo proste zadanie. Próbuję uzyskać wartość ze słownika, jeśli klucz istnieje. Robię to dla kilku kluczy w słowniku, a następnie tworząc obiekt, jeśli wszystkie istnieją (w zasadzie dekodowanie obiektu JSON). Jestem nowy w tym języku, ale wydaje mi się, że powinna działać, ale nie:Swift optionsals: problem z językiem lub coś złego?

class func fromDict(d: [String : AnyObject]!) -> Todo? { 
    let title = d["title"]? as? String 
    // etc... 
} 

To daje mi błąd: Operand of postfix ? should have optional type; type is (String, AnyObject)

Jednakże, jeśli mogę to zrobić, to działa:

class func fromDict(d: [String : AnyObject]!) -> Todo? { 
    let maybeTitle = d["title"]? 
    let title = maybeTitle as? String 
    // etc... 
} 

Wygląda na podstawową zamianę, ale może brakować jakiegoś niuansu języka. Czy ktokolwiek mógłby rzucić trochę światła na to?

+0

'd [" title "]' jest opcjonalne, używasz '?' Po opcjonalnym błędzie. Przypadkiem użycia dla tego operatora będzie "object? .property", więc będzie on oceniał tylko 'object.property', gdy' object' ma wartość ** not ** 'nil', inaczej zwraca' nil' – Binarian

Odpowiedz

3

Zalecany wzór jest

if let maybeTitle = d["title"] as? String { 
    // do something with maybeTitle 
} 
else { 
    // abort object creation 
} 

Jest to prawdopodobnie naprawdę kwestia niuansów. Forma array[subscript]? jest niejednoznaczna, ponieważ może oznaczać, że cały słownik (<String:AnyObject>) jest opcjonalny, podczas gdy prawdopodobnie masz na myśli wynik (String). W powyższym schemacie wykorzystujesz fakt, że Dictionary został zaprojektowany, aby zakładać, że uzyskiwanie dostępu do niektórych kluczowych wyników w opcjonalnym typie.

Po eksperymentach, a widząc, że ? po as jest tak niejednoznaczne, więcej, tu jest moje rozwiązanie:

var dictionary = ["one":"1", "two":"2"] 
// or var dictionary = ["one":1, "two":2] 
var message = "" 
if let three = dictionary["three"] as Any? { 
    message = "\(three)" 
} 
else { 
    message = "No three available." 
} 
message // "No three available." 

to będzie działać ze wszystkimi non-object Swift obiektów, w tym Swift struny, liczb itp Dziękuję Wiktorowi za przypomnienie mi , że String nie jest obiektem w Swift. +

Jeśli znasz typ wartości można zastąpić Any? z odpowiedniego typu opcjonalnego, jak String?

+0

Myślałem tak samo , ** ale ** powoduje błąd na placu zabaw, jak wspomniano o @vacawama. "NSString" w słowniku jako wartość jest pokazany jako typ '(String, AnyType)' i nie można go przenieść do 'String?' W tej samej linii. ... A twój * maybeTitle * może już nie będzie, inaczej nie zostanie ustawiony tuż przed wejściem do nawiasów. – Binarian

+0

Co jeśli słownik jest typu '[String: AnyObject]!' Jak słownik OP. To nie działa na plac zabaw dla mnie. – vacawama

+0

'jeśli wpuścisz trzy = słownik [" trzy "] jako NSObject?' Działa, jeśli słownik to '[String: AnyObject]!'. – vacawama

2

Istnieje kilka rzeczy się tu dzieje.

1) Urządzenie ? w d["title"]? nie jest prawidłowym użyciem. Jeśli próbujesz rozwinąć d["title"], użyj numeru !, ale zachowaj ostrożność, ponieważ spowoduje to awarię, jeśli title nie jest prawidłowym kluczem w słowniku. (? jest używany do opcjonalnego łączenia, jak gdybyś próbował wywołać metodę na opcjonalnej zmiennej lub uzyskać dostęp do właściwości.W takim przypadku dostęp nie zrobiłby nic, gdyby opcjonalne były nil). Wygląda na to, że nie próbujesz rozwinąć d["title"], więc zostaw sobie ?. Dostęp do słownika zawsze zwraca wartość opcjonalną, ponieważ klucz może nie istnieć.

2) Jeśli było ustalić, że:

let maybeTitle = d["title"] as? String 

Komunikat o błędzie zmienia się na: błędu: '(String, AnyObject)' nie jest zamienny do 'string'

Problem tutaj jest to, że String nie jest obiektem. Musisz rzucić na numer NSString.

let maybeTitle = d["title"] as? NSString 

To spowoduje maybeTitle bycia NSString?. Jeśli d["title"] nie istnieje lub typ faktycznie jest NSNumber zamiast NSString, opcjonalne będzie miało wartość nil, ale aplikacja nie ulegnie awarii.

3) Twoja wypowiedź:

let title = maybeTitle as? String 

nie rozpakować opcjonalną zmienną, jak chcesz. Poprawna forma to:

if let title = maybeTitle as? String { 
    // title is unwrapped and now has type String 
} 

więc oddanie że wszyscy razem:

if let title = d["title"] as? NSString { 
    // If we get here we know "title" is a valid key in the dictionary, and 
    // we got the type right. title has now been unwrapped and is ready to use 
} 

title będzie miał typ NSString co jest co jest zapisane w słowniku, ponieważ posiada ona przedmioty. Można zrobić prawie wszystko z NSString, że można zrobić z String, ale jeśli trzeba title być String można to zrobić:

if var title:String = d["title"] as? NSString { 
    title += " by Poe" 
} 

i jeśli słownik zawiera NSNumber s, a także:

if var age:Int = d["age"] as? NSNumber { 
    age += 1 
} 
+0

Ponieważ istnieje automatyczne tłumaczenie między NSString i String; zapisujesz NSString w ciągu znaków bez rzutowania. Używanie NSString w ogóle jest mylące, jeśli pożądany jest String. – Binarian

+1

To wstyd, że musimy używać 'NSString', ale to jest kwestia życia, gdy mamy do czynienia ze słownikami pochodzącymi z połączeń dotykowych Cocoa i Cocoa. Jeśli słownik byłby "[String: Any]" życie byłoby znacznie łatwiejsze. – vacawama

+0

Jak każdy NSObject, NSString jest już opcjonalny, więc nie ma potrzeby używania 'as?' Zamiast 'as ' – Binarian

Powiązane problemy