2017-09-26 2 views
1

Kann jemand die beste beste Art und Weise erklären ist dies so häufig auftretende Situation, wenn ich versuche gleiche json Objekt Kerndateneinheiten und PONSOsSonderbare Swift 4 Typ-System

so abzubilden,

id: Int = json["id"] as? Int // works 
id: Int32 = json["id"] as? Int32 // doesn't work 

als Ergebnis bin ich nicht in der Lage sowohl zu befriedigen Anforderungen gleichzeitig. Kann ich diese absolut sinnlose Situation in Swift 4 irgendwie vermeiden?

+1

Wenn Sie spezielle JSON Probleme haben, zeigen Sie bitte die JSON und den Code, den Sie benutzen, um es zu analysieren. – Sulthan

+1

Der obige Code zeigt nichts, was mit JSON zusammenhängt. '[String: Any]' ist "ein Wörterbuch von String zu absolut jedem Datentyp, der in Swift ausgedrückt werden kann."Das wird oft schlampig für JSON verwendet, aber das Parsen von JSON mit NSJSONSerialization sollte niemals das hier angegebene Wörterbuch erzeugen. Nichts darüber ist" sinnlos ", es sieht so aus, als ob es genau den Regeln folgt, daher kann es hilfreich sein, das Problem zu erklären Sie versuchen zu lösen. JSON hat keine 32-Bit- oder 64-Bit-Integer-Typen. –

+0

Beachten Sie, dass Swift's Int mit ObjCs NSInteger übereinstimmt und (wie in ObjC) der empfohlene Typ für fast alle "ganzzahligen" Dinge in Swift ist Von spezifischen Bitbreiten wird dringend abgeraten, es sei denn, sie sind für Interaktionen auf niedriger Ebene oder für sehr große Ganzzahlen auf einer 32-Bit-Plattform erforderlich.Sie sind nicht für den allgemeinen Einsatz in Swift konzipiert, sondern für den Fall, dass Sie sehr genaue Größen benötigen möchte gewarnt werden, wenn Sie Größenanforderungen verletzen. –

Antwort

5

Der Grund dafür ist verwirrend, weil as? in Swift zwei völlig orthogonale Bedeutungen hat, abhängig vom Kontext. Bei der rein mit Swift-Typen handeln, ist eine dynamische as? Besetzung, die nur nicht-nil zurückzugibt, wenn der Typ buchstäblich eine Instanz des Typs, auf der rechten Seite des Operators as? ist:

import Foundation 

let dict: [String : Any] = ["Foo" : 3 as Int] 

print(dict["Foo"] as? Int) 
print(dict["Foo"] as? Int32) 
print(dict["Foo"] as? Int64) 

Diese Optional(3) für die Rückkehr erstes Protokoll und nil für die anderen beiden, weil der Typ ein Int und kein Int32 oder ein Int64 ist.

Wenn jedoch die Art des Elements gegossen wird ein Objective-C Typ, dann ist as? nicht mehr eine strenge dynamische Umwandlung, sondern verursacht Überbrückung Verhalten:

import Foundation 

let dict: [String : Any] = ["Foo" : 3 as NSNumber] 

print(dict["Foo"] as? Int) 
print(dict["Foo"] as? Int32) 
print(dict["Foo"] as? Int64) 

Diese Erträge Optional(3) für alle drei Protokolle, da dies nicht mehr eine dynamische Besetzung ist - tatsächlich ist eine NSNumber-Instanz kein Mitglied eines der drei Typen, in die wir zu konvertieren versuchten. Stattdessen führt as? Swift zu Brücke der Objective-C-Typ, NSNumber, zu einem geeigneten Swift-Typ, wenn es möglich ist. Da Swift über eine Logik verfügt, die NSNumber mit Int, Int32 und Int64 (zusammen mit einer Reihe anderer numerischer Typen) überbrückt, erhalten wir Optional(3) für alle drei Protokolle. Wenn Sie jedoch versuchen, zu etwas wie Decimal zu werfen, für das es keine NSNumber Überbrückungslogik gibt, erhalten Sie noch nil.

Ein interessanter Nebeneffekt ist, dass as? nicht der transitive Eigenschaft folgt:

let foo: Int = 3 
print(foo as? NSNumber as? Int64) // Optional(3) 
print(foo as? Int64)    // nil 

Wie auch immer, wenn Sie Ihre Werte zu gieße NSNumber ersten, sollten Sie dann in der Lage sein, von dort zu einem werfen der numerischen Typen, die die Objective-C-Bridge unterstützt, was wahrscheinlich unter Ihrem alten Swift 3-Code unter der Haube vor sich ging. Alternativ, wenn Sie tatsächlich die Art wissen, dass der Wert sein soll, können Sie eine der anderen Integer-Typen initializers verwenden:

let dict: [String : Any] = ["Foo" : 3 as Int] 

let foo = (dict["Foo"] as? Int).map { Int32($0) }