Set property values of an Objective-C class using reflection - objective-c

I am trying to learn reflection in Objective-C. I've found some great information on how to dump the property list of a class, especially here, but I want to know if it is possible to set a property's value using reflection.
I have a dictionary of keys (property names) and values (all NSStrings). I want to use Reflection to get the property and then set its value to the value in my dictionary. Is this possible? Or am I dreaming?
This has nothing to do with the dictionary. I am merely using the dictionary to send in the values.
Like this question, but for Objective C.
- (void)populateProperty:(NSString *)value
{
Class clazz = [self class];
u_int count;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (int i = 0; i < count ; i++)
{
const char* propertyName = property_getName(properties[i]);
NSString *prop = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
// Here I have found my prop
// How do I populate it with value passed in?
}
free(properties);
}

Objective C properties automatically conform to the NSKeyValueCoding protocol. You can use setValue:forKey: to set any property value by a string property name.
NSDictionary * objectProperties = #{#"propertyName" : #"A value for property name",
#"anotherPropertyName" : #"MOAR VALUE"};
//Assuming class has properties propertyName and anotherPropertyName
NSObject * object = [[NSObject alloc] init];
for (NSString * propertyName in objectProperties.allKeys)
{
NSString * propertyValue = [objectProperties valueForKey:propertyName];
[object setValue:propertyValue
forKey:propertyName];
}

The NSKeyValueCoding protocol, which NSObject implements (see NSKeyValueCoding.h), contains the method -setValuesForKeysWithDictionary:. This method takes exactly the kind of dictionary you describe and sets the appropriate properties (or ivars) of the reciever.
This is absolutely reflection; the code in setValuesForKeysWithDictionary: accesses the properties by the names you give it, and will even find the appropriate ivar if no setter method exists.

Related

Get only the properties of an object that were declared in the header

I'm looking for a way to access, at runtime, only the properties of an object that were declared in the header file for that class. I was able to retrieve all the properties of an object via the following code:
MyTest *myTestObj = [[MyTest alloc] init];
myTestObj.prop1 = #"prop1";
myTestObj.prop2 = #"prop2";
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([myTestObj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
id keyValue = [myTestObj valueForKey:key];
if (keyValue != nil) {
[dict setObject:keyValue forKey:key];
}
}
free(properties);
(see Get an object properties list in Objective-C for more examples)
However, I need a way to limit the properties retrieved to only the ones declared in the .h file.
Basically, I'm looking for a way to access the object's public interface. Any suggestions on how to do this would be greatly appreciated.
I'm trying to save the state of my object in a dictionary so that it can be recreated later. Specifically, I'm trying to save it to the userInfo property of UILocalNotification.
I save the state of the object (in my case a UIViewController) to the userInfo property and create a local notification. When the user opens the app via a notification, I want to recreate the same UIViewController and set its state to what it was at the time the notification was created.
If there is a better way to do this that doesn't involve hardcoding then I'd love to hear some suggestions.
Declaring a property in the header or in a class extension (or category somewhere else) does not change the available runtime information of the property.
I need a way to limit the properties retrieved to only the ones declared in the .h file
There's no way to do that.
After edit:
On the other hand it's a very common pattern to just add a property or method that returns a set of keys to be serialized. You could, for example, make all your model classes implement a method like the following:
+ (NSArray *)persistentKeys
{
return #[ #"name", #"color", #"foo" ];
}
For each object to serialize your serialization code then has to walk the class and superclasses of the object and collect all persistent keys.
What you are trying to do is bizarre. Please have a look at the NSCoding protocol which is designed to do what you want to do. Looking at properties declared in the header file is a totally weird idea. You have no idea whether or not setting the public properties would have the result you want.

Is it okay to return a subclass from a class constructor that uses instancetype?

I have a class method in a category to construct a Cocoa collection in some way that the built-in initializers don't allow. Due to the limited initializer functionality, I have to use the mutable version of the collection to actually build it. Here's an example for NS{Mutable}IndexSet:
#implementation NSIndexSet (WSSNonContiguous)
+ (instancetype)WSSIndexSetFromMask:(NSUInteger)mask
{
NSMutableIndexSet * set = [NSMutableIndexSet indexSet];
for( NSUInteger i = 0; i < (sizeof(NSUInteger) * 8); i++ ){
if( mask & (1l << i) ){
[set addIndex:i];
}
}
return set;
}
My return type is sometimes a lie here -- there's always a mutable collection being returned, whether the user is requesting an immutable version or not.
Is it still appropriate to use instancetype in cases like this, or should I go with id? If I do use instancetype, should I also be explicitly re-creating the collection:
// Ick?
return [[self alloc] initWithIndexSet:set];
to make sure an immutable copy is returned when the call is +[NSIndexSet WSSIndexSetFromMask:]?
Everything is okay:
NSIndexSet *set = [[NSIndexSet WSSIndexSetFromMask:0] addIndex:0];
No visible #interface for 'NSIndexSet' declares the selector 'addIndex:'
instancetype says to the sender, that you return a instance of the receivers type even it is a subtype. For the sender it is a NSIndexSet, because it is send to the class object of NSIndexSet.
An introspection that way, that someone looks to the return type and sees a subclass and takes any advantage out of this information, is malformed. The contract is build with the return type and this is in this case NSIndexSet.

Way to get information on whether or not a property is set readonly in objective-c

I have got some properties in objective-c that are declared readonly such as:
#property (nonatomic, readonly) NSString* aProperty;
And I want to get information on the properties readability to treat them differently (e.g. change the way how information about them is displayed in the GUI). Thats why I need a way to ask if the property is readonly (I have got a list of property names and their objects in which they are contained). Is there a way to achieve this? - for example using the objective-c runtime.
objc_property_t prop = class_getProperty([object class], [aProperty UTF8String]);
if (!prop) {
// doesn't exist for object
return nil;
}
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:#","];
attrArray will contain all the properties refer to this Apple Doc
To make it easy & fast:
objc_property_t prop = class_getProperty([object class], "aProperty");
const char * roAttr *property_copyAttributeValue(property, "R");
I do not believe, that properties are encoded using UTF8. ;-) But it should not make any difference.
This way, you do not have to construct many instances of NSString.
You do not have to pick-up the desired attribute.
Always have a look to the headers. It is a part of the documentation. (property_copyAttribteValue() is only documentated at runtime.h.)
Consider using object_getClass() instead of [object class]. This depends on the task, you have to solve.

How to determine whether subclass of NSManagedObject has a particular property at runtime

I'm accustomed to using doesRespondToSelector: and instancesRespondToSelector: to determine at runtime whether objects have certain methods available. However, when using Core Data, I am not seeing the expected behavior for my #dynamic properties. For example, if I have a sortOrder property on my class, I can use the Objective-C runtime to see that this property exists at runtime. But if I ask the relevant Class object whether instancesRespondToSelector:, I get back NO. If I use the runtime to enumerate the available methods, none of my dynamic getters/setters appear in the list, which is consistent, but not what I expected.
My question, then, is: without using runtime inspection, is there an easy way to determine if an instance of an NSManagedObject subclass responds to getter/setter selectors that correspond to its #dynamic properties?
You can inspect NSManagmentObject though NSEntityDescription:
- (BOOL)hasPropertyWithName:(NSString *)name
{
NSEntityDescription *desc = self.entity;
return [desc.attributesByName objectForKey:name] != nil;
}
I have used the following method on NSManagedObject objects to retrieve a list of it's properties. Maybe it will point you in the right direction....
- (NSMutableArray *) propertyNames: (Class) class {
NSMutableArray *propertyNames = [[NSMutableArray alloc] init];
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
return [propertyNames autorelease];
}

How to detect a property return type in Objective-C

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?
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.
Cheap answer: use the NSObject+Properties source here.
It implements the same methodology described above.
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.
}
This is really a comment addressing an issue raised by Greg Maletic in response to answer provided by e.James 21APR09.
Agreed that Objective-C could use a better implementation for getting these attributes.
Below is a method I quickly threw together to retrieve attributes of a single object property:
- (NSArray*) attributesOfProp:(NSString*)propName ofObj:(id)obj{
objc_property_t prop = class_getProperty(obj.class, propName.UTF8String);
if (!prop) {
// doesn't exist for object
return nil;
}
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:#","];
return attrArray;
}
Partial list of attribute keys:
R Read-only
C Copy of last value assigned
& Reference to last value assigned
N Nonatomic property
W Weak reference
Full list at Apple
You can use isKindOfClass message
if([something isKindOfClass:NSArray.class])
[somethingElse action];
If you know that the property is defined :
id vfk = [object valueForKey:propertyName];
Class vfkClass = vfk.class;
And compare with isKindOfClass, isSubClass, etc.