Objective-C runtime issue setting object for NSMutableDictionary - objective-c

I am coding a fairly complex system that uses a lot of metadata to manage dynamic objects. I am using various objective-c runtime features. I want to add stuff to mutable dictionaries which are properties within various classes. I want to do this where I know the class type and I know the property name, but I don't want to 'hard code' the assignment. If I hard-coded it I could do this:
[[(myknownclass*)localClassObjectInstance knownDictionary] setObject:value forKey:key];
but what I want to do is something like this:
[[unknownClassObjectInstance {aStringContainingTheDictionaryName}] setObject:value forKey:key];
How can I reference the mutable dictionary property when I only have the name of the property at runtime?

You can use -valueForKey: to run the method:
[(NSMutableDictionary*)[inst valueForKey:#"whateverMethodName"] setObject:o forKey:k];
Or alternatively, you can use performSelector:
SEL dictGetter = NSSelectorFromString(#"whateverMethodName");
NSMutableDictionary* dict = [inst performSelector:dictGetter];
[dict setObject:o forKey:k];

Related

Transform NSCFDictionary to NSDictionary [duplicate]

I'm getting an NSCFDictionary returned to me and I can't figure out how to use it. I know it's of type NSCFDictionary because I printed the class and it came out as __NCSFDictionary. I can't figure out how to do anything with it.
I'm just trying to hold onto it for now but can't even get that to work:
NSDictionary *dict = [[NSURLCredentialStorage sharedCredentialStorage] allCredentials];
for(NSURLProtectionSpace key in [dict keyEnumerator])
{
NSCFDictionary *value = [dict objectForKey:key];
}
The class reference for allCredentials says its supposed to return a dictionary whose values are also dictionaries. My assignment statement isn't working though. Do I need a cast of some kind?
NSDictionary and the other collection classes are actually class clusters: several concrete subclasses classes masquerading under the interface of a single class: they all provide the same functionality (because they are subclasses of the same class — in NSDictionary's case, this involves the three "primitive methods" -count, -objectForKey:, and -keyEnumerator), but have different internal workings to be efficient in different situations, based on how they're created and what type of data they may be storing.
NSCFDictionary is simply a concrete subclass of NSDictionary. That is, your NSDictionaries may actually be NSCFDictionary instances, but you should treat them as instances of NSDictionary, because that will provide you with the required dictionary-storage functionality.
NSDictionary *value = [dict objectForKey:key];
Now, another reason your code doesn't work: NSURLProtectionSpace is a class, so you should use it as a pointer, like this:
for (NSURLProtectionSpace *key ...
NSCFDictionary is the private subclass of NSDictionary that implements the actual functionality. It's just an NSDictionary. Just about any NSDictionary you use will be an NSCFDictionary under the hood. It doesn't matter to you code. You can type the variable as NSDictionary and use it accordingly.
I have an NSCFDictionary that is actually a NSMutableDictionary object, I can delete items from it. I mention this to further clarify jtbandes' answer: the NSCFDictionary object may be any object that inherits from NSDictionary.

How do I convert a JSON structure to an object in Objective-C?

I have a JSON structure like this:
{
id:100
contactInfo: {
name: John Doe
city: New York
}
}
and a corresponding Customer class w/ the following properties including a nested contactInfo class.
Customer.id
Customer.contactInfo.name
Customer.contactInfo.city
Is it possible to convert the JSON data directly to an instance of the Customer class?
I know how to get an NSDictionary object, but that is very cumbersome and verbose to work with and would rather convert/deserialize the data directly to the Customer class.
EDITED w/ Additional Info:
In other languages I have worked with there is built-in support for deserialization of JSON to custom objects. All you have to do is mark the class as "Serializable" and the JSON is deserialized w/o the need to write custom code in each class.
The NSDictionary object can become very cumbersome if the class has nested classes. It would be much easier to use the custom object properties like this
Customer.contactInfo.name
rather than
[(NSDictionary*)[customerDict objectForKey:#"contactInfo"] objectForKey:#"name"]
thus my question about a standard built-in deserializer in Objective-C.
As of iOS5 there if official support for JSON
https://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html
I don't know why you think it is cumbersome to work with. A simple way to do it would be to have an initialiser for your customer object that took a dictionary as a parameter.
That way you could get your downloaded data as JSON, deserialise it into a dictionary and then pass it to the initializer to create an object from the dictionary.
Any of the dozens of JSON libraries will parse your JSON string and turn it into an NSDictionary. You're going to have to deal with an intermediate format if you don't want to write a parser yourself. But, once you have the NSDictionary you can use Key Value Coding (KVC) to set the properties on your object:
for (NSString *key in jsonDictionary) {
[customer setValue:[jsonDictionary objectForKey] forKey:key];
}
KVC is built into Cocoa, and defines setValue:forKey: for all objects, so you can set properties by name regardless of how they are defined.
You'll have to do something smarter than the loop above to handle your internal ContactInfo class, but you should be able to figure that out. For safety (since the JSON is probably coming over the network) you might want to make a whitelist of keys and loop over that, and use valueForKeyPath:/setValue:forKeyPath: to handle nested objects.
Consider using RestKit api (http://restkit.org/) and its object mapping system...
Here is some wiki : https://github.com/RestKit/RestKit/wiki/Object-mapping
Write an initializer for your Customer class that takes JSON data as a parameter, something like:
-(id)initWithJSONData:(NSData*)data;
You can implement that method to do what Abizern suggests: read the data into a dictionary, and then pull whatever values you need out of the dictionary.
If you want to get fancy, another way you could go is to adopt NSCoding in your Customer class, and then create a NSCoder subclass that deserializes JSON data. This could work very much like NSKeyedUnarchiver. Doesn't seem worth the trouble unless you're going to take the same approach with a number of your classes, though.
Using this library, you can do the following:
SBJSON *parser = [[SBJSON alloc] init];
NSDictionary *customer = [parser objectWithString:jsonString];
You can then access your properties using:
[customer objectForKey:#"id"];
[[customer objectForKey:#"contactInfo"] objectForKey:#"name"];
[[customer objectForKey:#"contactInfo"] objectForKey:#"city"];
You can then use this code in your Customer class' init function. For example:
- (id)initWithJSON:(NSString *)jsonString {
self = [super init]; // or whatever you need here
if (self) {
SBJSON *parser = [[SBJSON alloc] init];
NSDictionary *customer = [parser objectWithString:jsonString];
self.id = [customer objectForKey:#"id"];
self.contactInfo = [[NSDictionary alloc] initWithObjectsAndKeys:[[customer objectforKey:#"contactInfo"] objectForKey:#"name"], #"name", [[customer objectforKey:#"contactInfo"] objectForKey:#"city"], #"city"]
}
return self;
}

Is there any way to specify the class of objects of a NSMutableArray?

Im having the following problem:
I've made a NSMutableArray "array" that is going to contain objects of a class named "Class". At the start that array should be empty and it must be filled during the program's execution.
As I never actually told the compiler that my NSMutableArray will be holding elements of the class Class, when I try to write the appropriate methods the compiler wont let me do it.
This is my first experience on Objective-C and iPhone development. I used to code in C/C++ where I declared my arrays in the following way:
Class array[NUMBEROFELEMENTS];
Is there any way to do this in Objective-C?
Thanks!
The truth is that is doesn't matter to the NSMutableArray what type of object it is. NSMutableArray simply stores pointers to all the objects they contain, or reference.
The trick is when you pull the object back out of the array you need to create a new pointer based on the appropriate type:
MyObject *myObject = [myArray objectAtIndex:0];
Then you can use the object however you like:
[myObject doThatThingWithThisValue:10];
Or whatever you need.
Arrays in Objective-C Cocoa are objects (as well as other collections, sets, dictionaries). Arrays can contain references to objects of any type, so the type for the array is simply NSArray, NSMutableArray, etc...
Since they are objects, you can send them messages to manipulate their content.
I suggest you take a look at Apple's excellent Collections Programming Topics, which explain the rudiments of collections.
Here is a quick example :
// two objects of different types
NSNumber *n = [NSNumber numberWithInteger:10];
NSString *s = #"foo";
// alloc/init a new mutable array
NSMutableArray *a = [NSMutableArray arrayWithCapacity:10];
// add an object
[a addObject:n];
[a addObject:s];
// array a now contains a NSNumber and a NSString
Well, you can still have C-style arrays in Objective-C.
However, the characteristics of Objective-C (some people will call it strength, other will call it weakness) is that it has dynamic typing of objects and dynamic dispatch.
It has NSArray and NSMutableArray which are not specialized for the certain class. It can store objects of non-compatible classes.
You can use the following idiom: [obj isMemberOfClass: [Class type]] to make sure an array element is of the desired type and then cast to Class*.
You can also use for-each loop (aka Fast Enumeration):
NSMutableArray* array = //... initialize your array
for (Class* elm in array) {
elm.your_property = 10;
}

What is an NSCFDictionary?

I'm getting an NSCFDictionary returned to me and I can't figure out how to use it. I know it's of type NSCFDictionary because I printed the class and it came out as __NCSFDictionary. I can't figure out how to do anything with it.
I'm just trying to hold onto it for now but can't even get that to work:
NSDictionary *dict = [[NSURLCredentialStorage sharedCredentialStorage] allCredentials];
for(NSURLProtectionSpace key in [dict keyEnumerator])
{
NSCFDictionary *value = [dict objectForKey:key];
}
The class reference for allCredentials says its supposed to return a dictionary whose values are also dictionaries. My assignment statement isn't working though. Do I need a cast of some kind?
NSDictionary and the other collection classes are actually class clusters: several concrete subclasses classes masquerading under the interface of a single class: they all provide the same functionality (because they are subclasses of the same class — in NSDictionary's case, this involves the three "primitive methods" -count, -objectForKey:, and -keyEnumerator), but have different internal workings to be efficient in different situations, based on how they're created and what type of data they may be storing.
NSCFDictionary is simply a concrete subclass of NSDictionary. That is, your NSDictionaries may actually be NSCFDictionary instances, but you should treat them as instances of NSDictionary, because that will provide you with the required dictionary-storage functionality.
NSDictionary *value = [dict objectForKey:key];
Now, another reason your code doesn't work: NSURLProtectionSpace is a class, so you should use it as a pointer, like this:
for (NSURLProtectionSpace *key ...
NSCFDictionary is the private subclass of NSDictionary that implements the actual functionality. It's just an NSDictionary. Just about any NSDictionary you use will be an NSCFDictionary under the hood. It doesn't matter to you code. You can type the variable as NSDictionary and use it accordingly.
I have an NSCFDictionary that is actually a NSMutableDictionary object, I can delete items from it. I mention this to further clarify jtbandes' answer: the NSCFDictionary object may be any object that inherits from NSDictionary.

Store references to Class in Dictionary and then instantiate class later?

I would like to store a Class object in an NSMutableDictionary and then instantiate a copy later based on the key (specifically an NSNotification name). But unfortunately I can't find out what type of object 'Class' is in Objective-C, and its hard to google for. It's definitely not of a type 'id' so it won't go in the dictionary normally:
warning: Semantic Issue: Incompatible pointer types sending 'Class *' to parameter of type 'id'
Alternately I can store the Class' name in the dictionary, and use NSClassFromString to instantiate the class. It seems silly to use NSStringFromClass to put the Class in the Dictionary, and then NSClassFromString to get the class again. Is this my best option?
You can store a Class into a dictionary, e.g.
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
Class k = [NSString class];
[dict setObject:k forKey:#"foo"];
NSLog(#"%#", dict);
In fact, a Class is compatible with id. Note that you don't need a * after Class. It's like id.