2015-05-13 9 views
7

Ein NSNumber ein Bool enthält, wird mit anderen Arten leicht zu verwechseln, die in der Klasse NSNumber gewickelt werden kann:Gibt es eine korrekte Möglichkeit festzustellen, dass eine NSNummer von einem Bool mit Swift abgeleitet wurde?

NSNumber(bool:true).boolValue // true 
NSNumber(integer: 1).boolValue // true 
NSNumber(integer: 1) as? Bool // true 
NSNumber(bool:true) as? Int // 1 

NSNumber(bool:true).isEqualToNumber(1) // true 
NSNumber(integer: 1).isEqualToNumber(true) // true 

jedoch Informationen über seine ursprüngliche Art beibehalten wird, wie wir hier sehen können:

NSNumber(bool:true).objCType.memory == 99 // true 
NSNumber(bool:true).dynamicType.className() == "__NSCFBoolean" // true 
NSNumber(bool:true).isEqualToValue(true) || NSNumber(bool:true).isEqualToValue(false) //true 

Die Frage ist: Welcher dieser Ansätze ist der beste (und/oder sicherste) Ansatz, um festzustellen, wann ein Bool in eine NSNumber und nicht in etwas anderes eingebettet wurde? Sind alle gleich gültig? Oder gibt es eine andere, bessere Lösung?

Antwort

10

Sie können die gleiche Frage für Objective-C stellen, und hier ist eine Antwort in Objective-C - die Sie aufrufen oder in Swift übersetzen können.

NSNumber ist gebührenfrei zu CFNumberRef überbrückt, das ist ein weiterer Weg, um eine zu sagen NSNumber Objekt tatsächlich ein CFNumber ein (und umgekehrt).Jetzt hat CFNumberRef einen bestimmten Typ für booleans, CFBooleanRef, und dies wird verwendet, wenn ein boolean CFNumberRef aka NSNumber * ... und also alles, was Sie tun müssen, ist, ob Ihr NSNumber * ist eine Instanz CFBooleanRef:

- (BOOL) isBoolNumber:(NSNumber *)num 
{ 
    CFTypeID boolID = CFBooleanGetTypeID(); // the type ID of CFBoolean 
    CFTypeID numID = CFGetTypeID((__bridge CFTypeRef)(num)); // the type ID of num 
    return numID == boolID; 
} 

Hinweis: Sie stellen möglicherweise fest, dass NSNumber/CFNumber Objekte, die aus Booleschen erstellt werden, tatsächlich vordefinierte konstante Objekte sind; eine für YES, eine für NO. Sie könnten versucht sein, sich darauf zur Identifikation zu verlassen. Obwohl dies zur Zeit als zutreffend erscheint und in Apple's source code gezeigt ist, ist es nach unserem Kenntnisstand nicht dokumentiert, daher sollte man sich nicht darauf verlassen.

HTH

Nachtrag

Swift Code Übersetzung (von GoodbyeStackOverflow):

func isBoolNumber(num:NSNumber) -> Bool 
{ 
    let boolID = CFBooleanGetTypeID() // the type ID of CFBoolean 
    let numID = CFGetTypeID(num) // the type ID of num 
    return numID == boolID 
} 
+0

Ich habe dies als die richtige Antwort markiert, da Sie eine funktionierende Lösung bereitgestellt haben. Da es nicht in Swift ist, habe ich Ihre Antwort bearbeitet, um den Swift-Code hinzuzufügen (ich warte nur auf den Peer-Review dieser Änderung). Vielen Dank. – sketchyTech

+0

@GoodbyeStackOverflow - hat Ihre Übersetzung für Sie genehmigt. – CRD

+0

Ausgezeichnet, nochmals vielen Dank. – sketchyTech

0

Die erste ist die richtige.

NSNumber ist eine Objective-C-Klasse. Es ist für Objective-C gebaut. Es speichert den Typ mit den Typenkodierungen von Objective-C. So in Objctive-C die beste Lösung wäre:

number.objCType[0] == @encoding(BOOL)[0] // or string compare, what is not necessary here 

Dies stellt sicher, dass eine Änderung des Typs Codierung nach dem erneuten Kompilierung funktioniert.

AFAIK haben Sie nicht @encoding() in Swift. Sie müssen also ein Literal verwenden. Dies wird jedoch nicht unterbrochen, da @encoding() zum Zeitpunkt der Kompilierung ersetzt wird und das Ändern der Codierungen mit dem kompilierten Code abbricht. Unwahrscheinlich.

Der zweite Ansatz verwendet eine interne Kennung. Dies ist wahrscheinlich Gegenstand von Änderungen.

Ich denke, der dritte Ansatz wird falsch positive haben.

+0

Das Problem ist, dass '@encode (BOOL)' und '@encode (signed char)' und '@encode (char)' all return 'c' /' 99', (vorausgesetzt '-funsigned-char' wurde dem Compiler nicht gegeben). – dreamlax

+0

Dies ist eine Eigenschaft von 'NSNumber', die nicht geändert werden kann. Und damit sehe ich kein Problem. –

+0

Da OP speziell gefragt hat, ob eine 'NSNumber' ein' BOOL'-Objekt * umhüllt, anstatt etwas anderes *. – dreamlax

0

Verlassen Sie sich nicht auf den Klassennamen, da er wahrscheinlich zu einem Klassencluster gehört, und es handelt sich um ein Implementierungsdetail (und kann sich daher ändern).

Leider ist der Objective-C BOOL Typ war ursprünglich ein nur typedef für ein signed char in C, die immer als c codiert ist (dies ist der 99 Wert, den Sie sehen, da c in ASCII 99).

In modernem Objective-C, ich glaube, die BOOL Art ein tatsächlicher Booleschen Typ ist (das heißt nicht mehr nur ein typedef für signed char), aber für die Kompatibilität, es kodiert noch als c wenn zu @encode() gegeben.

Es gibt also keine Möglichkeit zu sagen, ob die 99 ursprünglich auf eine signed char oder ein BOOL bezeichnet, soweit NSNumber betrifft sie die gleichen sind.

Vielleicht, wenn Sie erklären, warum Sie wissen müssen, ob das NSNumber ursprünglich ein BOOL war, könnte es eine bessere Lösung geben.

+0

Der Grund dafür ist, dass bei der Verwendung von NSJSONSerialization Bools als NSNumber-Instanzen importiert werden und ich zwischen diesen unterscheiden muss, um mit JSON typsicher umzugehen, z. Nur erlaubt einem Bool, auf wahr oder falsch, nicht auf 10 oder 100, oder 999.99, zu wechseln und auch sicherzustellen, dass sie in exportierte JSON zurück in wahr und falsch, nicht in 1 und 0, transformiert werden. – sketchyTech

+0

Wenn Sie zurück in JSON umwandeln, rufen Sie '-boolValue' auf der' NSNumber' auf, um eine 1 oder eine 0 zu erhalten. –

+1

Wie ich es aus der Dokumentation verstehe, bietet boolValue einfach eine bool Interpretation einer NSNumber-Instanz: "A 0 value always bedeutet falsch und jeder Wert ungleich Null wird als wahr interpretiert. " Aber ich möchte die wahre/falsche Darstellung beibehalten und Bools und Zahlen nicht verwechseln. – sketchyTech

Verwandte Themen