lmirosevic
lmirosevic

Reputation: 16317

How to check if an object is a Foundation Object in Cocoa?

Is there a way to find out whether an arbitrary object in Cocoa is a Foundation object? I'm talking about NSString, NSArray, NSDictionary, etc.

Let me elaborate a bit more...

Foundation objects (AFAIK) have some shared characteristics: They all implement the NSCoding protocol, they can all go into a PList, etc.

So for instance if I wanted to archive an object graph to disk, it would be useful to make sure that any objects I add to the object graph are either Foundation objects or my custom objects which I've implemented NSCoding on and it seems silly to do:

if ([myObject isKindOfClass:[NSString class]] || 
    [myObject isKindOfClass:[NSNumber class]] || 
    [myObject isKindOfClass:[NSArray class]] || 
    [myObject isKindOfClass:[NSDictionary class]] || 
    [myObject isKindOfClass:[NSSet class]] || 
    ...) {
    //add myObject to object graph
}

That's a simple but maybe useless example, the problem I'm having is in some code I've written that converts a dictionary into a url params string using a format string with the %@ specifier, but I don't wan't arbitrary objects to go in there because I don't want memory addresses in my url params:

//convert dictionary into url params string
[postDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    if ([key isKindOfClass:[NSString class]] && ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]])) {
        [tempPost appendFormat:@"%@=%@&", key, obj];
    }
    else {
        *stop = TRUE;
    }
}];
NSString *post;
if ([tempPost length] > 0) {
    post = [tempPost substringToIndex:[tempPost length] - 1];
}

Upvotes: 0

Views: 1345

Answers (4)

Peter Hosey
Peter Hosey

Reputation: 96373

Foundation objects (AFAIK) have some shared characteristics: They all implement the NSCoding protocol, they can all go into a PList, etc.

Neither of those is true. Only some classes conform to NSCoding, not all of which are in Foundation, and only a select few classes are property-list classes.

You can certainly generate a plist from an object using one of Apple's archivers, but then the object must conform to NSCoding—see above.

So for instance if I wanted to archive an object graph to disk, it would be useful to make sure that any objects I add to the object graph are either Foundation objects or my custom objects which I've implemented NSCoding on …

You can test whether an object conforms to NSCoding with [someObject conformsToProtocol:@protocol(NSCoding)].

If you need to test whether an object is a property-list object, you'll have to do that by testing class membership, like you showed in your question. Such a test will include Core Foundation property-list objects, and that is correct: CF property-list objects are just as valid in a property list as their Foundation counterparts, largely because CF and Cocoa property-list objects are interchangeable (due to the toll-free bridge).

The full list of valid property list classes is in both the Property List Programming Guide and the Property List Programming Topics for Core Foundation. The class names are different, of course (CFString vs. NSString), but because of toll-free bridging, they are interchangeable.

For all intents and purposes, an NSString is a CFString and vice versa, and the same for every other pair of toll-free-bridged classes. It isn't helpful to think of “NSStrings vs. CFStrings”; instead, remember that they are the same thing, and treat them as such.

the problem I'm having is in some code I've written that converts a dictionary into a url params string using a format string with the %@ specifier, but I don't wan't arbitrary objects to go in there because I don't want memory addresses in my url params…

That's a valid case for class-membership testing.

//convert dictionary into url params string
    if ([key isKindOfClass:[NSString class]] && ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]])) {
        [tempPost appendFormat:@"%@=%@&", key, obj];
    }

You will find an & at the end of your URL that way.

I previously described a specification for an object that formats URL query strings. You can include your class-membership tests within your implementation of that object.

Upvotes: 1

Vincent Bernier
Vincent Bernier

Reputation: 8664

You can always ask for it's class.
But you should not base you code on that to make some logic decision a path to the dark side, that is...
But Seriously, Apple is exposing NSString as a cluster class which mean that under the hood it's a lot of different classes and any one of them could be return to you, and there is no guaranty that in the future they will be the same, the naming could change, etc.
Even if I doubt that an old class like NSString would have it's underlaying structure changed, I wouldn't rule that possibility out and place a bet on it.

Upvotes: 1

stevex
stevex

Reputation: 5827

Depending on what you're trying to do - if you're writing some sort of diagnostic and are just looking to exclude the core classes, you could try just getting the name of the class and looking at the first few letters.

if ([someObj.class.description hasPrefix:@"NS"]) {
  NSLog(@"Well, it's an NSSomething object...");
}

Upvotes: 1

paulbailey
paulbailey

Reputation: 5346

I'm not sure exactly what you mean here. Are you asking which classes are toll-free bridged?

Upvotes: 1

Related Questions