2010-07-13 12 views
5

Ho scritto una macro in Objective-C per eseguire un cast sicuro. Ecco come si presenta finora:Objective-C Safe Casting Macro

#define SAFE_CAST(OBJECT, TYPE) ([OBJECT isKindOfClass:[TYPE class]] ? (TYPE *) OBJECT: nil) 

Questo funziona davvero bene, ma sarebbe bello se ci fosse un modo per memorizzare oggetto in una variabile in modo che non ha ottenuto chiamato due volte. Ad esempio, utilizzando la macro come ad esempio:

NSString *str = SAFE_CAST([dictinary objectForKey:key], NSString); 

risultati in codice simile a questo quando la macro viene espansa:

NSString *str = ([[dictinary objectForKey:key] isKindOfClass:[NSString class]] ? (NSString *) [dictinary objectForKey:key]: nil); 

preferirei per farlo funzionare più come questo:

id obj = [dictionary objectForKey:key]; 
NSString *str = ([obj objectForKey:key] isKindOfClass[NSString class]] ? (NSString *) obj : nil); 

Grazie.

+0

non vedo quale sia il punto di questo è. Hai detto che vuoi usarlo per disinfettare i plists, ma sicuramente se lo usi devi testare se l'oggetto restituito è nullo? Perché non testare solo se l'oggetto è KindOfClass: expectedClass? – JeremyP

risposta

8

È possibile utilizzare l'estensione GCC chiamato dichiarazione statement expressions avere

#define SAFE_CAST(OBJECT, TYPE) ({ id obj=OBJECT;[obj isKindOfClass:[TYPE class]] ? (TYPE *) obj: nil; }) 

Detto questo, penso che sia in generale un approccio male avere una situazione in cui è necessario utilizzare SAFE_CAST molto. Non mettere mai oggetti di classi diverse in una matrice; non riutilizzare mai un messaggio di azione (IBAction)someAction:(id)sender per oggetti UI di classi diverse. Quindi di solito non è necessario utilizzare SAFE_CAST.

+1

In questo momento sto usando questa macro quando leggo le informazioni da un plist. Non sono garantito che i contenuti del plist siano validi. È come qualsiasi altro cast; può essere abusato, ma ha i suoi usi. Se c'è un modo migliore per farlo, potresti elaborarlo per favore? Come da risposta, le espressioni delle dichiarazioni sembrano interessanti. Possono essere usati con LLVM? – LandonSchropp

+0

Sì, funziona con 'clang'. Per quanto riguarda 'plist', sono d'accordo con l'uso di' SAFE_CAST' se è fornito dall'utente. Ma se non lo è, penso che sia meglio usare 'NSAssert' mentre si sta sviluppando, in modo che il programma si blocchi troppo quando preparo un plist non valido; la versione build della tua app non dovrebbe avere bisogno di 'SAFE_CAST', perché ti assicuri che' plist' sia valido. – Yuji

+0

Funziona alla grande. Grazie. – LandonSchropp

0

Quindi scrivilo così, basta avvolgerlo in do {} while (0) < - e non solo tra parentesi.

#define SAFE_CAST(OBJECT, TYPE, VAR) do { \ 
    id obj = OBJECT; \ 
    VAR = [obj isKindOfClass:[TYPE class]] ? (TYPE *) obj : nil; \ 
} while(0) 
+0

Non riesco a farlo compilare correttamente nell'uso che ho usato sopra. Il compilatore emette: errore: espressione prevista prima di 'fare'. – LandonSchropp

+0

Err hai ragione, mi dispiace. Prima c'è un errore di sintassi dopo zero, mancante; e in secondo luogo, ha bisogno di stare da solo. Significa, o passare un altro argomento che assegna l'output dell'operazione ternaria, o farlo in un modo diverso. :) – jer

+0

Nessun problema. Grazie comunque per la risposta. – LandonSchropp

4

Se davvero che si deve fare questo, è possibile utilizzare una funzione:

#define SAFE_CAST(Object, Type) (Type *)cast_helper(Object, [Type class]) 
static id cast_helper(id x, Class c) { 
    return [x isKindOfClass:c] ? x : nil; 
} 
+0

Questo è un approccio interessante, ma una macro sembrava un po 'più pulita. Sto cercando di evitare di far fluire le funzioni globali. Inoltre, potresti per favore approfondire cosa intendi con 'Se pensi davvero di dover fare questo'?Diverse altre lingue supportano il casting sicuro, inclusi C# e C++. Perché non dovrei usarli? – LandonSchropp

+1

@helixed: perché pensi che una macro globale in agguato sia meglio di una funzione globale in agguato? – JeremyP

+0

@helixed: per l'approccio plist citato sopra si adatta, è proprio la situazione in cui quei calchi dinamici sono davvero necessari sono rari. Però non vedo il problema specifico con una funzione globale - preferisco usare le funzioni piuttosto che i macro che fanno solo una semplice elaborazione del testo. –