33

I have a method that receives a NSArray of Class objects and I need to check if they all are Class type generated with the code bellow:

NSMutableArray *arr = [[NSMutableArray alloc] init];

[arr addObject:[NSObject class]];
[arr addObject:[NSValue class]];
[arr addObject:[NSNumber class]];
[arr addObject:[NSPredicate class]];
[arr addObject:@"not a class object"];

The problem is that Class is not an objective-c class, it is a struc, so I can not use just

    for (int i; i<[arr count]; i++) {
        Class obj = [arr objectAtIndex:i];

        if([obj isKindOfClass: [Class class]]) {
            //do sth
        }
    }

So, I need to I check if the obj variable is a Class type, I suppose it will be in C directly, but how can I do that?

It will be a plus if the answer also provide a way to check if the item in the array is a NSObject, as the items in the example code, the NSPredicate would also be true for the NSObject check

Felipe Sabino
  • 17,825
  • 6
  • 78
  • 112
  • 1
    you say that `Class` is not an obj-C class, so the question is a little bit confusing. What does the array hold, and what do you want to compare it to? – Dan Rosenstark Jun 30 '11 at 14:31
  • 1
    +1 Great question with nonobvious answer. – Ben Zotto Jun 30 '11 at 15:00
  • Can you say more about what you intend to then do with the array of class objects? There is an answer to your actual question (calling @bbum!) but it might be less practical than discussing the best way to use them depending on how you plan to do so. – Ben Zotto Jun 30 '11 at 15:12
  • possible duplicate of http://stackoverflow.com/questions/355312/in-objective-c-how-can-i-tell-the-difference-between-a-class-and-an-instance-of – user102008 Jul 22 '11 at 22:51

5 Answers5

46

To determine if an "object" is a class or an instance you need to check if it is a meta class in a two stage process. First call object_getClass then check if it is a meta class using class_isMetaClass. You will need to #import <objc/runtime.h>.

NSObject *object = [[NSObject alloc] init];
Class class = [NSObject class];

BOOL yup = class_isMetaClass(object_getClass(class));
BOOL nope = class_isMetaClass(object_getClass(object));

Both Class and *id have the same struct layout (Class isa), therefore can pose as objects and can both receive messages making it hard to determine which is which. This seems to be the only way I was able to get consistent results.

EDIT:

Here is your original example with the check:

NSMutableArray *arr = [[NSMutableArray alloc] init];

[arr addObject:[NSObject class]];
[arr addObject:[NSValue class]];
[arr addObject:[NSNumber class]];
[arr addObject:[NSPredicate class]];
[arr addObject:@"not a class object"];

for (int i; i<[arr count]; i++) {
    id obj = [arr objectAtIndex:i];

    if(class_isMetaClass(object_getClass(obj)))
    {
        //do sth
        NSLog(@"Class: %@", obj);
    }
    else
    {
        NSLog(@"Instance: %@", obj);
    }
}

[arr release];

And the output:

Class: NSObject
Class: NSValue
Class: NSNumber
Class: NSPredicate
Instance: not a class object

Pang
  • 9,564
  • 146
  • 81
  • 122
Joe
  • 56,979
  • 9
  • 128
  • 135
  • 1
    very good explanation! and I can also check if the object inherits from `NSObject`, for example, using `[object_getClass(obj) isSubclassOfClass:[NSObject class]]`. tks! – Felipe Sabino Jun 30 '11 at 16:40
  • @Felipe Sabina I would stick with class_isMetaClass because not every object is guaranteed to be an NSObject, that is why there is `id` and an `NSObject` protocol. Check out `NSProxy` which is a root class of its own http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSProxy_Class/Reference/Reference.html – Joe Jul 01 '11 at 12:25
  • @Joe I didn't know about NSProxy, nice to learn that! but actually I used NSObject just to make the example easier, I will use it to validade my classes from core data, so every class will have to be an NSManagedObject type, then the rule will apply. But tks for pointing out! – Felipe Sabino Jul 01 '11 at 12:45
7

Update: In iOS 8+ or OS X 10.10+, you can just do:

object_isClass(obj)

(You will need to #import <objc/runtime.h>.)

user102008
  • 30,736
  • 10
  • 83
  • 104
5

Joe’s answer is good. A simple alternative that works in most situations is to check if the object returns itself in response to class.

if ([obj class] == obj) { … }

This works because the NSObject meta-class overrides class and returns the class object (itself) — see the NSObject Class Reference. This does not require the runtime headers, but assumes the objects are subclasses of NSObject and don’t override -class or +class and do something unusual.


With the input in the question, the results are the same as Joe’s:

NSMutableArray *arr = [[NSMutableArray alloc] init];

[arr addObject:[NSObject class]];
[arr addObject:[NSValue class]];
[arr addObject:[NSNumber class]];
[arr addObject:[NSPredicate class]];
[arr addObject:@"not a class object"];

for (id<NSObject> obj in arr) {

    if ([obj class] == obj) {
        NSLog(@"Class: %@", obj);
    }
    else {
        NSLog(@"Instance: %@", obj);
    }
}

Class: NSObject
Class: NSValue
Class: NSNumber
Class: NSPredicate
Instance: not a class object

Douglas Hill
  • 1,537
  • 10
  • 14
1

If you need to verify if the object in the array is a Class object then you can verify if it responds to class methods.

for ( id obj in arr ) {
    if (([obj respondsToSelector:@selector(isSubclassOfClass:)])
          && (obj == [NSObject class]) ) {
        NSLog(@"%@", obj);
    }
}

Once you know it is a Class object by verifying if it responds to isSubclassOfClass: then you can check for direct equality with [NSObject class].

Deepak Danduprolu
  • 44,595
  • 12
  • 101
  • 105
  • And without the '&& (obj == [NSObject class])' check it gives all classes. How/why does respondsToSelector: work on a class? Does class implement NSObject *protocol*? – Eiko Jun 30 '11 at 15:54
  • @Eiko Class objects can perform any of the methods defined in the root class provided they don't clash with the class methods. – Deepak Danduprolu Jun 30 '11 at 16:40
  • @Eiko: the root class means the root class in the inheritance hierarchy. It is usually NSObject, but it is possible to create new root classes. – user102008 Jul 22 '11 at 22:40
  • @user102008 That's actually my question - what *is* the root class of class? – Eiko Jul 24 '11 at 11:02
  • @Eiko I was talking of `NSObject`. I can't find the exact developer doc that mentions it but you can look at this [`link`](http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html) that explains it a bit. – Deepak Danduprolu Jul 25 '11 at 03:29
-4

No problem here is how you do it:

if([NSStringFromClass([obj class]) isEqualToString:@"Class"]){
    NSLog(@"It is type of Class");
}

Edit

Or you can make your Class conform to a protocol: and check if obj that you get from the array conforms to this protocol like this:

if([obj conformsToProtocol:@protocol(MyClassProtocol)])

Edit

Or you can check if what you get from the array is an NSObject complient

if ([object conformsToProtocol:@protocol(NSObject)]) {
    // Do something
}
Cyprian
  • 9,423
  • 4
  • 39
  • 73
  • I was hoping for a check similar to `isKindOfClass:` method. You see, if `obj` is the result of `[NSObject class]`, the `NSStringFromClass([obj class])` will result in the string "NSObject" – Felipe Sabino Jun 30 '11 at 14:41
  • Did you try it? I was using it once when receiving object of type UIView I was checking if it was my custom UIView (MyCustomUIView) and it worked. – Cyprian Jun 30 '11 at 15:08
  • By the way you can use a protocol. Make your class conform to a protocol and then check if the object that you get from the array. conforms to this protocol. This way you will get what you want.Check the answer edit – Cyprian Jun 30 '11 at 15:10
  • Pls, check my second Edit I think that is waht you are looking for – Cyprian Jun 30 '11 at 15:17
  • I need to use it with classes from the foundation framework as well... so I would have to create protocols and categories for those classes, which would be a enormous hassle even though it is a nice hack. tks ;) – Felipe Sabino Jun 30 '11 at 17:08
  • "No problem here is how you do it:" That will not work. There is no class named "Class". And calling `[... class]` on a class object will only return itself (since `+class` overrides the other `class` method). – user102008 Jul 22 '11 at 22:38
  • "and check if obj that you get from the array conforms to this protocol like this:" A *class object* is an instance of a meta-class; how are you going to make it conform to a protocol? – user102008 Jul 22 '11 at 22:39
  • "Or you can check if what you get from the array is an NSObject complient" all objects are NSObject compliant. this doesn't say anything – user102008 Jul 22 '11 at 22:39
  • @user102008 I don't know if you can read, but if you have read the question you would notice that he has created a class called "Class" hence your first argument is invalid. Second, if you read carefully again, I use object "Class" not "class", to check if the object conforms to the protocol, hence you are wrong again. I give you credit for you third comment, that was my mistake. – Cyprian Jul 23 '11 at 07:25
  • As you guys can check from the accepted answer, its comments and references, not all objects as NSObject compliant ;) – Felipe Sabino Jan 17 '12 at 13:16