2012-04-06 22 views
7

Quiero escribir una función o una directiva como NSLog() que toma cualquier tipo de variable, primitivas y objetos. En esa función, quiero distinguirlos.Objetivo-C: Cómo comprobar si una variable es un objeto, una estructura u otra primitiva

sé cómo funciona para objetos:

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

pero ¿cómo puedo distinguir objetos a partir de estructuras o incluso enteros o flotadores. Algo así como:

"isKindOfStruct:CGRect" or "isInt" 

por ejemplo?

¿Esto es posible? Pensé que ya que puede enviar todo a NSLog (@ "...", objetos, ints, structs) debe ser posible?

¡Gracias por cualquier ayuda!

EDITAR

Mi objetivo final es poner en práctica algún tipo de polimorfismo.

Quiero ser capaz de llamar a mi función:

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

or [self MYFUNCTION:int]... 

y en mi_funcion

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

Sé que isKindOf no existe y ni siquiera se puede realizar tales métodos en los primitivos . Tampoco estoy seguro sobre el "???" tipo genérico de "valor" en el encabezado de la función.

¿Es esto posible?

+0

. isKindOfClass comprueba si una clase es un miembro o una subclase, y isMemberOfClass comprueba si una clase es * exactamente * un tipo de clase. https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html –

Respuesta

3

Una función como NSLog() puede indicar qué tipos esperar en su lista de parámetros de la cadena de formato que pasa como el primer parámetro. Por lo tanto, no consulta el parámetro para averiguar su tipo: calcula qué tipo espera en función de la cadena de formato y luego interpreta el parámetro en consecuencia.

+0

OK, eso tiene sentido. ¿Hay alguna manera de enviar solo una variable genérica y distinguir el tipo más adelante? – Django

+0

Sugiero usar 'NSValue' para convertir cualquier valor que no sea objeto en objetos. Sería más fácil dar un consejo sólido si explicaras en tu pregunta lo que realmente estás tratando de lograr. – Caleb

2

No se puede pasar una estructura C o una primitiva como un parámetro de tipo id. Para hacerlo, deberá envolver la primitiva en un objeto NSNumber o NSValue.

p. Ej.

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

id se define como un puntero a un objeto Objetivo-C.

+0

Sé que no puedes hacer eso. ID es solo para objetos, no para primitivos. Entonces, ¿no hay un tipo genérico para todas las variables que puedo distinguir más adelante? – Django

+0

No. Podrías pasar un 'void *', que es un puntero a cualquier cosa, pero no habría manera de saber qué era una vez que lo obtuviste. – joerick

+1

NSValue puede tomar cualquier dato con su método 'valueWithBytes: objCType:', pero debe pasar el tipo '@ encode' y el puntero' void * '. – joerick

0

Es importante tener en cuenta que id representa cualquier objeto Objective-C. Y por objeto Objective-C, me refiero a uno que se define mediante @interface. No representa una estructura o tipo primitivo (int, char, etc.).

Además, solo puede enviar mensajes (la [...] sintaxis) a objetos Objective-C, por lo que no puede enviar el mensaje isKindOf: a una estructura o primitiva normal.

Pero puede convertir un entero, etc., en un NSNumber, un char * en un NSString y ajustar una estructura dentro de una clase NSObject-dervied. Entonces serán objetos 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 

Además, la salida de The Most Useful Objective-C Code I’ve Ever Written para algunas cosas muy práctico que utiliza la directiva @encode() compilador (que) devuelve una cadena que describe cualquier tipo que se le da ..."

LOG_EXPR Vicente Gable (x) es una macro que imprime x, sin importar el tipo x, sin tener que preocuparse por las cadenas de formato (y las fallas relacionadas de, por ejemplo, imprimir una cadena C de la misma manera que una NSString). Funciona en Mac OS X y iOS.

1

#define IS_OBJECT (x) (strchr ("@ #", @encode (typeof (x)) [0])! = NULL) Este micro funciona, y lo obtuve en algún lugar del desbordamiento de la pila.

3

@alex gray answer no funcionó (o al menos no funcionó en iOS SDK 8.0). Puedes usar la respuesta de @ deepax11, pero quiero señalar cómo funciona esta 'macro mágica'. Se basa en las codificaciones de tipo proporcionadas por el sistema. Según la documentación de Apple:

Para ayudar al sistema en tiempo de ejecución, el compilador codifica los tipos de retorno y argumento para cada método en una cadena de caracteres y asocia la cadena con el selector de método. El esquema de codificación que utiliza también es útil en otros contextos y, por lo tanto, se pone a disposición del público con la directiva del compilador @encode(). Cuando se le da una especificación de tipo, @encode() devuelve una cadena de codificación de ese tipo. El tipo puede ser un tipo básico, como un int, un puntero, una estructura o unión etiquetada, o un nombre de clase; cualquier tipo, de hecho, que se pueda usar como argumento para el operador C sizeof().

para romper la macro aparte, lo primero que sale "TypeOf" nuestra variable, a continuación, llamar @encode() de ese tipo, y finalmente comparar el valor devuelto a 'objeto' y 'clase' tipos de tabla de codificación.

Ejemplo completo debe verse como:

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 
    } 

Tenga en cuenta que NSInteger volverá int en los dispositivos de 32 bits, y largo en los dispositivos de 64 bits. Lista completa de codificaciones:

‘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) 

Leer isKindOf sí existe más sobre Type Encodings at Apple

Cuestiones relacionadas