Objective-C: how to check if a variable is an object, structure or other primitive

I want to write a function or directive, such as NSLog (), which accepts any variables, primitives and objects. In this function, I want to distinguish them.

I know how this works for objects:

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

but how to distinguish objects from structures or even an integer or float. Something like:

 "isKindOfStruct:CGRect" or "isInt" 

eg?

Is it possible? I thought, since you can send everything to NSLog (@ "...", objects, ints, structs), should this be possible?

Thanks for any help!

EDIT

My ultimate goal is to realize some kind of polymorphism.

I want to be able to call my function:

 MY_FUNCTION(int) MY_FUNCTION(CGRect) MY_FUNCTION(NSString *) ... or [self MYFUNCTION:int]... 

and in MY_FUNCTION

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

I know that isKindOf does not exist, and you cannot even execute such methods on primitives. I'm also not sure about "???" The general type of "value" in the function header.

Is it possible?

+7
source share
6 answers
 #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) ?@ "YES":@"NO"); // -> NO NSLog(@"%@", IS_OBJECT(b) ?@ "YES":@"NO"); // -> YES NSLog(@"%@", IS_OBJECT(c) ?@ "YES":@"NO"); // -> NO 

Also, check out the Vincent Gable Ive Ever Written's most useful Objective-C code for some very useful materials that use the @encode() compiler directive (which) returns a string describing any type of it ... "

LOG_EXPR (x) is a macro that outputs x, regardless of the type of x, without having to worry about format strings (and associated crashes, for example, when printing a C-string in the same way as NSString). It works on Mac OS X and iOS.

+8
source

A function of type NSLog() can specify which types to expect in the parameter list from the format string that you pass as the first parameter. Thus, you do not request a parameter to determine its type - you determine what type you expect based on the format string, and then you interpret the parameter accordingly.

+3
source

@alex gray's answer didn't work (or at least didn't work with iOS SDK 8.0). You can use the @ deepax11 answer, however I want to indicate how this โ€œmagic macroโ€ works. It relies on type encodings provided by the system. According to Apple documentation:

To help the runtime system, the compiler encodes the return and argument types for each method in a character string and binds the string using a method selector. The coding scheme used is also useful in other contexts and therefore becomes publicly available using the @encode () compiler directive. When a type specification is given, @encode () returns a string code of that type. A type can be a base type, such as an int, a pointer, a labeled structure or union, or a class name โ€” any type that actually can be used as an argument to the C sizeof () operator.

To split the macro separately, we first get "typeOf" our variable, then call @encode () of this type and finally compare the return value with the types "object" and "class" from the encoding table.

A complete example should look like this:

  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 } 

Please note that NSInteger will return int on 32-bit devices and long on 64-bit devices. Full list of encodings:

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

Read more about Apple's Encoding Type

+3
source

You cannot pass a C structure or primitive as an id parameter. To do this, you have to transfer the primitive to an NSNumber or NSValue object.

eg.

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

id defined as a pointer to an Objective-C object.

+2
source

#define IS_OBJECT (x) (strchr ("@ #", @encode ( typeof (x)) [0])! = NULL) This micro works, and I ended up in a stack overflow somewhere.

+1
source

It is important to note that id represents any Objective-C object. And with the Objective-C object, I mean the one that is defined using @interface. It is not a structural or primitive type (int, char, etc.).

In addition, you can send messages (syntax [...]) to Objective-C objects, so you cannot send the message isKindOf: to a normal structure or primitive.

But you can convert an integer, etc. in NSNumber, char * in NSString and wrap the structure inside the NSObject-dervied class. Then they will be Objective-C objects.

0
source

All Articles