2012-04-06 8 views
7

Voglio scrivere una funzione o una direttiva come NSLog() che accetta qualsiasi tipo di variabile, primitive e oggetti. In quella funzione voglio distinguerli.Objective-C: come verificare se una variabile è un oggetto, una struct o un'altra primitiva

so come funziona per gli oggetti:

- (void)test:(id)object { 
    if ([object isKindOfClass:[NSString class]]) 
     ... 

ma come faccio a distinguere gli oggetti da struct o addirittura interi o galleggianti. Qualcosa del tipo:

"isKindOfStruct:CGRect" or "isInt" 

per esempio?

È possibile? Ho pensato che dal momento che è possibile inviare tutto a NSLog (@ "...", oggetti, interi, strutture) deve essere possibile?

Grazie per qualsiasi aiuto!

EDIT

Il mio obiettivo finale è quello di attuare una sorta di polimorfismo.

Voglio essere in grado di chiamare la mia funzione:

MY_FUNCTION(int) 
MY_FUNCTION(CGRect) 
MY_FUNCTION(NSString *) 
... 

or [self MYFUNCTION:int]... 

e in my_function

-(void)MYFUNCTION:(???)value { 
    if ([value isKindOf:int]) 
     ... 
    else if ([value isKindOf:CGRect]) 
     ... 
    else if ([value isKindOfClass:[NSString class]]) 
     ... 
} 

so che IsKindOf non esiste e non si può nemmeno eseguire tali metodi su primitive . Non sono nemmeno sicuro del "???" tipo generico di "valore" nell'intestazione della funzione.

È possibile?

+0

IsKindOf esiste. isKindOfClass verifica se una classe è un membro o una sottoclasse e isMemberOfClass controlla se una classe è * esattamente * un tipo di classe. https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html –

risposta

3

Una funzione come NSLog() può indicare quali tipi aspettarsi nel proprio elenco di parametri dalla stringa di formato passata come primo parametro. Quindi non si esegue una query del parametro per capire che è di tipo: è possibile determinare il tipo previsto in base alla stringa di formato e quindi interpretare il parametro di conseguenza.

+0

OK, ha senso. C'è un modo per inviare solo una variabile generica e distinguere il tipo in seguito? – Django

+0

Suggerirei di usare 'NSValue' per convertire qualsiasi valore non-oggetto in oggetto. Sarebbe più semplice dare consigli solidi se spiegassi nella tua domanda cosa stai cercando di realizzare. – Caleb

2

Non è possibile passare una struttura C o primitiva come parametro di tipo id. Per fare ciò, dovrai avvolgere la primitiva in un oggetto NSNumber o NSValue.

ad es.

[self test: [NSNumber numberWithInt: 3.0]]; 

id è definito come un puntatore a un oggetto Objective-C.

+0

So che non puoi farlo. L'ID è solo per oggetti non per primitivi. Quindi non esiste un tipo generico per tutte le variabili che posso distinguere in seguito? – Django

+0

No. Potresti passare un 'void *', che è un puntatore a qualsiasi cosa, ma non ci sarebbe modo di dire cosa fosse una volta che l'avresti ottenuto. – joerick

+1

NSValue può prendere qualsiasi dato con il suo metodo 'valueWithBytes: objCType:', ma devi passare il tipo '@ encode' come pure il puntatore' void * '. – joerick

0

È importante notare che id rappresenta qualsiasi oggetto Objective-C. E per oggetto Objective-C, intendo quello che viene definito usando @interface. Non rappresenta una struttura o un tipo primitivo (int, char, ecc.).

Inoltre, è possibile inviare messaggi (la [...] sintassi) solo agli oggetti Objective-C, quindi non è possibile inviare isKindOf: messaggio a una normale struttura o primitiva.

Ma è possibile convertire un numero intero ecc. In un NSNumber, un char * in una NSString e avvolgere una struttura all'interno di una classe NSObject-dervied. Quindi saranno oggetti Objective-C.

8
#define IS_OBJECT(T) _Generic((T), id: YES, default: NO) 

NSRect a = (NSRect){1,2,3,4}; 
NSString* b = @"whatAmI?"; 
NSInteger c = 9; 

NSLog(@"%@", IS_OBJECT(a)[email protected]"YES":@"NO"); // -> NO 
NSLog(@"%@", IS_OBJECT(b)[email protected]"YES":@"NO"); // -> YES 
NSLog(@"%@", IS_OBJECT(c)[email protected]"YES":@"NO"); // -> NO 

Inoltre, controlla di The Most Useful Objective-C Code I’ve Ever Written per alcune cose molto pratico che utilizza la direttiva @encode() compilatore (che) restituisce una stringa che descrive qualsiasi tipo è dato ..."

LOG_EXPR Vincent Gable (x) è una macro che stampa x, indipendentemente dal tipo x, senza doversi preoccupare delle stringhe di formato (e dei relativi arresti anomali, ad esempio stampando una C-string allo stesso modo di una NSString). Funziona su Mac OS X e iOS

1

#define IS_OBJECT (x) (strchr ("@ #", @encode (typeof (x)) [0])! = NULL) Questo micro funziona che ho ottenuto da qualche parte nello stack overflow.

3

@alex risposta grigia non ha funzionato (o almeno non ha funzionato su iOS SDK 8.0). Puoi usare @ deepax11 answer, tuttavia voglio indicare come funziona questa "macro magica". Si basa su codifiche di tipo fornite dal sistema. Come da documentazione Apple:

Per aiutare il sistema di runtime, il compilatore codifica i tipi di ritorno e argomento per ogni metodo in una stringa di caratteri e associa la stringa al selettore di metodo. Lo schema di codifica che usa è utile anche in altri contesti e quindi è reso pubblicamente disponibile con la direttiva del compilatore @encode(). Quando viene fornita una specifica di tipo, @encode() restituisce una codifica stringa di quel tipo. Il tipo può essere un tipo di base come int, un puntatore, una struttura o unione con tag o un nome di classe, qualsiasi tipo, infatti, che può essere utilizzato come argomento per l'operatore C sizeof().

per rompere la macro a parte, per prima cosa otteniamo "typeof" nostra variabile, quindi chiamare @encode() su quel tipo, e, infine, confrontare il valore restituito al 'oggetto' e il tipo 'classe' dalla tabella di codifica.

esempio completo dovrebbe essere simile:

const char* myType = @encode(typeof(myVar));//myVar declared somewhere 
    if([@"@" isEqualToString:@(myType)] || [@"#" isEqualToString:@(myType)]) 
    { 
     //myVar is object(id) or a Class 
    } 
    else if (NSNotFound != [[NSString stringWithFormat:@"%s", myType] rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"{}"]].location) 
    { 
     //myVar is struct 
    } 
    else if ([@"i" isEqualToString:@(myType)]) 
    { 
     //my var is int 
    } 

prega di notare che NSInteger tornerà int su dispositivi a 32 bit, e lunga su dispositivi a 64 bit. Lista completa delle codifiche:

‘c’ - char 
‘i’ - int 
’s’ - short 
‘l’ - long 
‘q’ - long long 
‘C’ - unsigned char 
‘I’ - unsigned int 
’S’ - unsigned short 
‘L’ - unsigned long 
‘Q’ - unsigned long long 
‘f’ - float 
‘d’ - double 
‘B’ - C++ bool or a C99 _Bool 
‘v’ - void 
‘*’ - character string(char *) 
‘@’ - object(whether statically typed or typed id) 
‘#’ - class object(Class) 
‘:’ - method selector(SEL) 
‘[<some-type>]’ - array 
‘{<some-name>=<type1><type2>}’ - struct 
‘bnum’ - bit field of <num> bits 
‘^type’ - pointer to <type> 
‘?’ - unknown type(may be used for function pointers) 

Leggi di più riguardo Type Encodings at Apple