2015-05-13 18 views
7

Un NSNumber contenente un Bool viene facilmente confuso con altri tipi che possono essere avvolti nella classe NSNumber:Esiste un modo corretto per determinare che un NSNumber è derivato da un Bool usando Swift?

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 

Tuttavia, è trattenuto informazioni sul suo tipo originale, come si può vedere qui:

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 

La domanda è: quale di questi approcci è l'approccio migliore (e/o più sicuro) per determinare quando un Bool è stato inserito all'interno di un NSNumber piuttosto che qualcos'altro? Sono tutti ugualmente validi? O c'è un'altra soluzione migliore?

risposta

10

Puoi fare la stessa domanda per Objective-C, ed ecco una risposta in Objective-C - che puoi chiamare da, o tradurre in, Swift.

NSNumber è verde ponte a CFNumberRef, che è un altro modo di dire un oggetto NSNumber è infatti un CFNumber uno (e viceversa).Ora CFNumberRef ha un tipo specifico per booleani, CFBooleanRef, e questo viene utilizzato durante la creazione di un valore booleano CFNumberRef aka NSNumber * ... Quindi tutto quello che dovete fare è controllare se il vostro NSNumber * è un'istanza di 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; 
} 

Nota: È possibile notare che gli oggetti NSNumber/CFNumber creati da booleani sono in realtà oggetti costanti predefiniti; uno per YES, uno per NO. Potresti essere tentato di fare affidamento su questo per l'identificazione. Tuttavia, anche se attualmente sembra essere vero, ed è mostrato in Apple's source code, a nostra conoscenza è non documentato quindi non ci si dovrebbe fare affidamento.

HTH

Addendum

Swift traduzione del codice (per 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

L'ho contrassegnato come la risposta corretta in quanto hai fornito una soluzione funzionante. Poiché non è in Swift, ho modificato la tua risposta per includere il codice Swift (aspettando solo la revisione tra pari di quella modifica). Grazie. – sketchyTech

+0

@GoodbyeStackOverflow - ha approvato la traduzione per te. – CRD

+0

Eccellente, grazie ancora. – sketchyTech

0

Il primo è quello corretto.

NSNumber è una classe Objective-C. È costruito per Objective-C. Memorizza il tipo usando le codifiche di tipo di Objective-C. Così in Objctive-C la soluzione migliore sarebbe:

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

Questo assicura che un cambiamento della codifica tipo funzionerà dopo la ri-compilazione.

AFAIK non hai @encoding() in Swift. Quindi devi usare un letterale. Tuttavia, questo non si interromperà, perché @encoding() viene sostituito in fase di compilazione e la modifica delle codifiche si interromperà con il codice compilato. Improbabile.

Il secondo approccio utilizza un identificativo interno. Questo è probabilmente soggetto di cambiamenti.

Penso che il terzo approccio avrà falsi positivi.

+0

Il problema è che '@encode (BOOL)' e '@encode (char firmato)' e '@encode (char)' tutti restituiscono 'c' /' 99', (fornito '-funsigned-char' non è stato dato al compilatore). – dreamlax

+0

Questa è una proprietà di 'NSNumber' che non può essere modificata. E non vedo un problema con quello. –

+0

Poiché l'OP ha chiesto espressamente di determinare se un 'NSNumber' sta avvolgendo un oggetto' BOOL' piuttosto che qualcos'altro *. – dreamlax

0

Non fare affidamento sul nome della classe poiché probabilmente appartiene a un cluster di classe ed è un dettaglio di implementazione (e quindi soggetto a modifiche).

Purtroppo, il tipo di Objective-C BOOL era in origine un solo typedef per un signed char in C, che è sempre codificato come c (questo è il valore 99 che state vedendo, dal momento che c in ASCII è 99).

Nella moderna Objective-C, credo che il tipo BOOL è un tipo booleano effettivo (cioè non è più solo un typedef per signed char), ma per la compatibilità, esso codifica ancora come c quando somministrato a @encode().

Quindi, non c'è modo di dire se il 99 origine di cui un signed char o un BOOL, per quanto NSNumber è interessato sono la stessa cosa.

Forse se si spiega perché è necessario sapere se lo NSNumber era originariamente un BOOL, potrebbe esserci una soluzione migliore.

+0

Il motivo è dovuto al fatto che quando si utilizza NSJSONSerialization Bool vengono importati come istanze NSNumber e devo distinguere tra questi per trattare con JSON in un modo sicuro, ad es. solo consentendo a un Bool di passare a true o false non 10 o 100 o 999,99, e anche di assicurarsi che vengano trasformati di nuovo in true e false non in 1 e 0 in JSON esportato. – sketchyTech

+0

Quando si ritorna a JSON, chiama '-boolValue' su' NSNumber' per ottenere 1 o 0. –

+1

Come ho capito dalla documentazione, boolValue fornisce semplicemente un'interpretazione boolea di qualsiasi istanza NSNumber: "Valore 0 sempre significa falso, e qualsiasi valore diverso da zero viene interpretato come vero. " Ma voglio mantenere la rappresentazione vero/falso e non confondere bool e numeri. – sketchyTech