2011-01-13 12 views
12

Ho scritto una grande applicazione per iPhone di social networking, e uno dei maggiori problemi che ho incontrato è il fatto che NSInteger (e tutti gli altri tipi NS-non-object) non sono di prima classe cittadini. Questo problema deriva dal fatto che, ovviamente, non hanno alcuna rappresentazione per un valore nullo.Risolvendo NSInteger <-> NSNumero problema

Questo crea due problemi principali:

  1. Tonnellate di overhead e opacità per convertire da e verso NSNumber durante la memorizzazione/recupero da una collezione.
  2. Impossibile rappresentare nil. Spesso, voglio essere in grado di rappresentare un valore "non impostato".

Un modo per risolvere questo è utilizzare NSNumber tutto il tempo, ma questo diventa estremamente confuso. In un oggetto modello Utente, avrei circa 20 NSNumbers diversi, e nessun modo semplice per dire se ognuno è un float, intero, bool, ecc.

Quindi ecco i miei pensieri per le potenziali soluzioni e i pro/contro . Non sono davvero venduto su nessuno di essi, quindi ho pensato di chiedere un feedback e/o soluzioni alternative a questo problema.

  1. Continuare a utilizzare i tipi NSInteger e utilizzare solo NSIntegerMax per rappresentare nil.
    PRO - Meno memoria in testa
    PRO - Digitazione chiara
    CON - NSIntegerMax non è proprio nulla. Se i programmatori non stanno attenti o non conoscono questa convenzione, i valori non validi potrebbero perdere nel livello di visualizzazione.
    CON - non è in grado di memorizzare in una collezione senza conversioni in entrata e in uscita

  2. Usa NSNumber e designare i tipi che usano la notazione ungherese (ad esempio NSNumber fHeight, NSNumber iage)
    PRO - cittadini di prima classe
    PRO - problema risolto Nil
    CON - Aumento overhead di memoria
    CON - perdere tipo compilatore verifica
    CON - notazione ungherese è controversa

  3. scrivere la mia prima classe tipi di oggetti primitivi (si pensi Java http://developer.android.com/reference/java/lang/Integer.html)
    PRO - cittadini di prima classe
    PRO - Nil problema risolto
    PRO - Mantiene tipo compilatore verifica
    PRO - Oggetti sarà più semplice di NSNumber. La memoria interna sarà specifica per il tipo di dati.
    CON - Aumento della memoria in testa
    CON - sacrifica un po 'di portabilità dei codici e la compatibilità

Alla ricerca di un argomento convincente a favore di una di queste tecniche, o uno che non ho pensato a se' ce l'ho.


UPDATE

Sono andato avanti e ha iniziato un progetto open source (Apache 2.0), in cui tratterò un numero delle nostre classi interne come ho tempo. Attualmente include wrapper di oggetti per alcuni dei più comuni tipi di dati nativi (BOOL, CGFloat, NSInteger, NSUInteger). Abbiamo scelto di farlo perché aggiorna questi tipi di dati a cittadini di prima classe con una tipizzazione rigorosa. Forse non sei d'accordo con questo approccio, ma ha funzionato bene per noi, quindi sentiti libero di usarlo se vuoi.

Sto aggiungendo altre classi che abbiamo trovato usi per, tra cui una cache su disco-backed LRU, un oggetto "Pair", un pool di rilascio di memoria insufficiente, ecc

Godetevi github - Zoosk/ZSFoundation

+0

questo mi sembra community wiki :) – LordT

+2

Se desideri che i numeri siano cittadini di prima classe in Objective-C, invia una richiesta di funzionalità all'indirizzo http://bugreport.apple.com. Sarà contrassegnato come duplicato, ma maggiore è il numero di duplicati, meglio è (dal momento che ricorda agli sviluppatori della lingua che le persone hanno bisogno di determinate funzionalità). –

+0

@DougW, stai pensando di aggiungere a ZSFloat 'compare: withPrecision'? –

risposta

5

La convenzione più comune per rappresentare l'idea di nil come NSInteger consiste nell'utilizzare il valore NSNotFound. Questo è, in effetti, uguale a NSIntegerMax, anche se tende a essere più ovvio per il lettore che si tratta di un valore sentinella che rappresenta la mancanza di un numero. Ci sono molti casi in cui questo è usato in tutto Cocoa. Un caso comune è il campo di posizione di un NSRange come valore di ritorno da -rangeOfString: e altri.

+0

Ehi, sì, sono d'accordo, ma alla fine questo è un convegno e non necessariamente sicuro. Abbiamo riscontrato un numero di bug in cui l'altezza di un utente potrebbe essere accidentalmente visualizzata come NSIntegerMax, ad esempio perché il programmatore che scrive la logica del livello di visualizzazione non era a conoscenza della convenzione. – DougW

+0

Sembra che tu abbia bisogno di educare i tuoi programmatori, allora. Non si dovrebbero mai fare ipotesi cieche sui valori che vengono usati. –

+0

Heh, okay. Non è una questione di educazione, è una questione di diffusione della conoscenza. Alcuni NSIntegers possono usare questa convenzione e altri no. E che mi dici di CGFloats? Se nessuno posterà qualcosa di meglio, andrò avanti e segnerò la risposta, dato che potrebbe non esserci soluzione migliore. – DougW

4

Potresti provare questo?

#define numberWithFloat(float f) \ 
    [NSNumber numberWithFloat:f] 
#define floatFromNumber(NSNumber *n) \ 
    [n floatValue] 

(vedi la mia risposta originale sotto)

Ecco l'altra cosa con NSNumber, non c'è bisogno di recuperare quello che si imposta.

Per esempio

NSNumber *myInt = [NSNumber numberWithInteger:100]; 
float myFloat = [myInt floatValue]; 

è perfettamente valida. La forza di NSNumber è che ti permette di "debellare" le tue primitive, usa compare:, usa isEqualTo: e per una facile visualizzazione.


[EDIT]

utente @ Dave DeLong afferma che sub-classing NSNumber è un Bad Idea senza molto lavoro. Dato che è un class cluster (che significa NSNumber è una superclasse astratta di molte sottoclassi) dovrai dichiarare la tua memoria se la sottoclassi. Non consigliato, e grazie a Dave per averlo indicato.

+0

Questa è una cattiva idea senza molto più lavoro. 'NSNumber' fa parte di un cluster di classi e [la documentazione contiene informazioni su cosa devi fare di più] (http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class /Reference/Reference.html#//apple_ref/doc/uid/20000178-2025) –

+0

@Dave, sì, l'ho appena verificato. Non ho idea di cosa significhi, però. C'è qualcosa in Class Cluster? –

+2

@Stephen [La definizione della documentazione di un Class Cluster] (http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ClassCluster.html) –

3

In alternativa, esiste anche la struttura NSDecimal per rappresentare i tipi numerici. NSDecimal (e la sua versione di classe Objective-C NSDecimalNumber) consente di rappresentare i decimali reali ed evitare errori in virgola mobile, quindi tende ad essere recommended for dealing with things like currency.

La struttura NSDecimal può rappresentare numeri e uno stato Non numero (una sostituzione nullo potenziale). È possibile stabilire se un NSDecimal non è un numero che utilizza NSDecimalIsNotANumber() e generare il valore Not a Number con l'aiuto di un NSDecimalNumber.

NSDecimali sono faster to work with than NSDecimalNumbers e le strutture non presentano lo stesso tipo di problemi di gestione della memoria che gli oggetti fanno.

Tuttavia, non esiste un modo semplice per ottenere valori in formato NSDecimal senza utilizzare un NSDecimalNumber temporaneo. Inoltre, molte funzioni matematiche (come le operazioni di trigonometria) disponibili per numeri in virgola mobile semplici non sono ancora disponibili per NSDecimal. Mi piacerebbe scrivere le mie funzioni che aggiungono alcune di queste funzionalità, ma richiedono l'accesso ai campi interni della struttura.Apple li etichetta come privati ​​(con l'uso di un trattino basso), ma sono presenti nelle intestazioni e immagino che difficilmente cambieranno in futuro.

Aggiornamento: Dave DeLong ha scritto una serie di funzioni per l'esecuzione di trigonometria, radici e altre operazioni NSDecimal. Le ho ottimizzate per fornire fino a 34 cifre di precisione decimale e il codice per queste funzioni può essere scaricato here. Come avvertimento, questa precisione ha un prezzo in termini di prestazioni, ma queste funzioni dovrebbero andare bene per calcoli abbastanza rari.

+0

Se scrivi le tue funzioni trigonometriche per 'NSDecimal', mi piacerebbe saperlo! –

+0

Interessante, non ho usato NSDecimal prima. Non sono sicuro che risolva il mio problema, ma grazie per averlo indicato. – DougW

+0

NSDecimalNumber è una sottoclasse di NSNumber. – JeremyP

0

Non capisco perché, se si memorizzano cose come un NSNumber, è necessario preoccuparsi di che tipo è. NSNumber sceglierà una rappresentazione interna appropriata e quindi eseguirà tutte le conversioni necessarie a seconda del formato richiesto.

Se non è possibile indicare quale tipo è appropriato dal nome della variabile, è necessario scegliere nomi migliori . per esempio.

numberOfChildren = [NSNumber numberWithFloat: 2.5]; 

è chiaramente stupido.

E per i valori di valuta, è consigliabile utilizzare NSDecimalNumber (che è una sottoclasse di NSNumber) ma definisce le operazioni aritmetiche di base 10 in modo da non doversi preoccupare dell'arrotondamento a virgola mobile.

+0

Grazie per la risposta Jeremy. Penso che se guardi alle polemiche sulla notazione ungherese, troverai molti argomenti contro l'uso di una convenzione di denominazione esplicita sul tipo di variabile. Certo, numberOfChildren è ovvio, ma che ne dite di "heightInInches"? È un numero intero o consente di ottenere pollici frazionari? Esistono diversi casi in cui l'intervallo di valori consentito non è chiaro, anche con una buona denominazione. La ragione per cui mi interessa è che voglio essere esplicito su cose come signed vs unsigned e int vs float across the stack. – DougW

+0

Aggiungerò che ci sono un certo numero di lingue debolmente o anche non tipizzate, e questo è un approccio perfettamente valido. C e Obj C tuttavia sono progettati per essere fortemente tipizzati e ci sono molte ragioni per non rompere questo costrutto. Quindi non sto dicendo che non potresti farlo a modo tuo, preferisco semplicemente entrare nel regno della tipizzazione forte. Ciò porta comunque a un altro dibattito, quindi per la mia particolare domanda mi piacerebbe attenermi a ciò che è forte. – DougW

Problemi correlati