views:

1155

answers:

4

Hello,

I have an object in objective-c at runtime, from which I only know the KVC key and I need to detect the return value type (e.g. I need to know if its an NSArray or NSMutableArray) of this property, how can I do that?

+1  A: 

You can use isKindOfClass message

if([something isKindOfClass:[NSArray class]])
     [somethingElse action];
Mykola Golubyev
the problem is, the object is newly created, so also the property value is nil. I just have a KVC Key, from which I event don't know if the object really has this accessors, but if it has it, I need to know the type of this return value without having the according object in hand.
Enyra
Why not to make your logic decision later when you will have an object? Objective C is more runtime language then static.
Mykola Golubyev
@Enyra: that's okay. Using the above, if the object is nil, it will return false even if it is eventually going to be an array.
Jason Coco
[myObject valueForKey:@"theKnownKey"] is nil for sure, so i can make following:[[myObject valueForKey:@"theKnownKey"] isKindOf:[NSArray class]] ?
Enyra
@Mykola: I'm reading an xml file and filling it into objects of classes I don't know during runtime, when I fill in the property value of the class, I need to check if I can fill in the new value / object directly into the property, or if I have to create an array and fill in the object into the array, so I can not wait until I have an object value :)
Enyra
I assume that you want to mutate the array but don't want to throw an exception? Instead, why not use respondsToSelector: to see if your mutation selector is available? If not, do whatever you want. If so, feel free to mutate (call) it.
Jason Coco
I tend to think that without "dummy" object with non-nil value you won't be able to retrieve runtime data type.
Mykola Golubyev
Not only mutate, I create a class by NSClassForString and instantiate it. If I iterate thru the children elements, I check want to check if the according property value type is an array or not. Since I can not control, which classes I create, I also can not assume, that the array instance already has been created and if not, i want to create it myself
Enyra
Hmmm... you could use the runtime to get information about a property, but I'm not sure that's really what you'd want either...
Jason Coco
+2  A: 

The preferred way is to use the methods defined in the NSObject Protocol.

Specifically, to determine if something is either an instance of a class or of a subclass of that class, you use -isKindOfClass:. To determine if something is an instance of a particular class, and only that class (ie: not a subclass), use -isMemberOfClass:

So, for your case, you'd want to do something like this:

// Using -isKindOfClass since NSMutableArray subclasses should probably
// be handled by the NSMutableArray code, not the NSArray code
if ([anObject isKindOfClass:[NSMutableArray class]]) {
    // Stuff for NSMutableArray here
} else if ([anObject isKindOfClass:[NSArray class]]) {
    // Stuff for NSArray here

    // If you know for certain that anObject can only be
    // an NSArray or NSMutableArray, you could of course
    // just make this an else statement.
}
Matt Ball
In Enyra case 'anObject' is nil. Look comments in my respond.
Mykola Golubyev
This test will not work. All arrays — even immutable ones — are descended from NSMutableArray.
Chuck
isMemberOfClass: is the check which ignore inheritance.
Ross Boucher
+4  A: 

You're talking about runtime property introspection, which happens to be something that Objective-C is very good at.

In the case you describe, I'm assuming you have a class like this:

@interface MyClass
{
    NSArray * stuff;
}
@property (retain) NSArray * stuff;
@end

Which gets encoded in XML something like this:

<class>
    <name>MyClass</name>
    <key>stuff</key>
</class>

From this information, you want to recreate the class and also give it an appropriate value for stuff.

Here's how it might look:

#import <objc/runtime.h>

// ...

Class objectClass;       // read from XML (equal to MyClass)
NSString * accessorKey;  // read from XML (equals @"stuff")

objc_property_t theProperty =
    class_getProperty(objectClass, [accessorKey UTF8String]);

const char * propertyAttrs = property_getAttributes(theProperty);
// at this point, propertyAttrs is equal to: T@"NSArray",&,Vstuff
// thanks to Jason Coco for providing the correct string

// ... code to assign the property based on this information

Apple's documentation (linked above) has all of the dirty details about what you can expect to see in propertyAttrs.

e.James
Actually, it would be equal to T@"NSArray",-)
Jason Coco
Thank you, you are absolutely right. I've made the change
e.James
Thanks, this seems to be the best way, I'm going to try it.Btw. your example is quite near to what I'm doing, but it can be any xml file, so my key is the element name.
Enyra
How can I produce this @"NSArray" type string with @encode()? I tried @enocde(NSArray *) an dit did not work.
Enyra
I'm not sure if you can do that with @encode. Your best bet is to ask another question here on SO :)
e.James
That would be the next step ;)
Enyra
I take issue with the assessment that Objective-C is "very good at" property introspection. A good implementation would more closely resemble 'propArray = [object properties]' and return an array of property objects, each with metadata describing the property in detail. Instead, our property is a mysterious 'objc_property_t'. To get it, you have to take your prop name and pull the UTF8 version of it. The resultant descriptor is an arcane string, which I have to parse myself? Yes, Objective-C lets you introspect properties, but there is nothing about its implementation I would consider good.
Greg Maletic
+3  A: 

Cheap answer: use the NSObject+Properties source here.

It implements the same methodology described above.

Jim Dovey