Django
Django

Reputation: 545

Objective-C: How to check if a variable is an object, a struct or another primitive

I want to write a function or a directive like NSLog() that takes any kind of variable, primitives and objects. In that function I want to distinguish those.

I know how it works for objects:

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

but how do I distinguish objects from structs or even integer or floats. Something like:

"isKindOfStruct:CGRect" or "isInt" 

for example?

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

Thanks for any help!

EDIT

My ultimate goal is to implement 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 doesn't exists and you can't even perform such methods on primitives. I'm also not sure about the "???" generic type of "value" in the function header.

Is that possible?

Upvotes: 8

Views: 8605

Answers (6)

hris.to
hris.to

Reputation: 6363

@alex gray answer did not work(or at least did not work on iOS SDK 8.0). You can use @deepax11 answer, however I want to point how this 'magic macro' works. It relies on type encodings provided from the system. As per the Apple documentation:

To assist the runtime system, the compiler encodes the return and argument types for each method in a character string and associates the string with the method selector. The coding scheme it uses is also useful in other contexts and so is made publicly available with the @encode() compiler directive. When given a type specification, @encode() returns a string encoding that type. The type can be a basic type such as an int, a pointer, a tagged structure or union, or a class name—any type, in fact, that can be used as an argument to the C sizeof() operator.

To break the macro apart, we first get "typeOf" our variable, then call @encode() on that type, and finally compare returned value to 'object' and 'class' types from encoding table.

Full example should look like:

    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 Type Encodings at Apple

Upvotes: 2

deepax11
deepax11

Reputation: 307

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

Upvotes: 1

Alex Gray
Alex Gray

Reputation: 16463

#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 Vincent Gable's The Most Useful Objective-C Code I’ve Ever Written for some very handy stuff that uses the @encode() compiler directive (that) returns a string describing any type it’s given..."

LOG_EXPR(x) is a macro that prints out x, no matter what type x is, without having to worry about format-strings (and related crashes from eg. printing a C-string the same way as an NSString). It works on Mac OS X and iOS.

Upvotes: 8

Cthutu
Cthutu

Reputation: 8907

It's important to note that id represents any Objective-C object. And by Objective-C object, I mean one that is defined using @interface. It does not represent a struct or primitive type (int, char etc).

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

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

Upvotes: 0

joerick
joerick

Reputation: 16448

You can't pass a C struct or primitive as a parameter of type id. To do so, you'll have to wrap the primitive in an NSNumber or NSValue object.

e.g.

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

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

Upvotes: 2

Caleb
Caleb

Reputation: 124997

A function like NSLog() can tell what types to expect in its parameter list from the format string that you pass as the first parameter. So you don't query the parameter to figure out it's type -- you figure out what type you expect based on the format string, and then you interpret the parameter accordingly.

Upvotes: 3

Related Questions