Sending message to objectForKey to NSMutableArray instance, And it works!! Strange? - iphone

I have a very bad code written in my program, was just playing around as I am learning Objective C and iOS platform. What I did is,
I have created NSMutableArray like this,
placeInfo = [NSMutableArray array];
and than later in my code I am doing something like this, basically I am manipulating Google places api response(JSON).
NSDictionary *results = [responseString JSONValue];
placeInfo = [results objectForKey:#"result"];
self.phoneNumber = (NSString *)[placeInfo objectForKey:#"formatted_phone_number"]; // In this line compiler warns me that NSMutableArray might not response to this.
I checked documentation but I didn't find objectForKey in NSMutableArray.
So what could be the reason? Why my code isn't crashing? Why it is returning phone number by "formatted_phone_number" key?
EDIT
After first answer I have edited my code and added type casting like this, but it still works.
NSDictionary *results = [responseString JSONValue];
placeInfo = (NSMutableArray *)[results objectForKey:#"result"];
self.phoneNumber = (NSString *)[placeInfo objectForKey:#"formatted_phone_number"];

I’ve never used the Google Places API, but I’d guess [results objectForKey:#"result"] actually returns another dictionary, so the objectForKey: works.
Because objective-c just uses pointers to refer to objects, it’s never actually being converted to an NSMutableArray. Also, objective-c doesn't know at compile time if a method will exist, due to its dynamic nature (you can actually add methods and even whole classes at runtime).
Depending on the compiler settings, it may just show a warning that objectForKey: might not be found at runtime, and let it continue compiling anyway. It ends up working just fine if you actually passed it an NSDictionary.
Even when you put the (NSMutableArray *) cast in front of it, it won’t change anything. That simply casts the pointer type, and doesn’t actually change the object in any way.

It's doing this because [results objectForKey:#"result"] is returning you something that is not an NSMutableArray. That something that's being returned is likely another NSDictionary which, of course, does respond to objectForKey: To find out what you've got, set a breakpoint after result = [placeInfo objectForKey:#"result"] and inspect result. The debugger will tell you what kind of object you're dealing with. I'll bet you anything you like that it's an NSDictionary.
Objective C allows you to send any message (called a selector) to any object at any time; the runtime does not care whether a particular object implements a given selector. If the target object does not respond to a given selector it will ignore it. It will not crash. In this respect it's utterly unlike most other OOP languages, including C++ and Java, which will fail to compile if you try to call a method that a particular class doesn't implement. You can find out if an object responds to a given selector (which is analagous to using introspection to see if a given class implements a certain method) by saying [result respondsToSelector:#selector(objectForKey:)]. This difference between methods and messages is critically important to understanding Objective C. I'd recommend reading The Objective C Programming Language before doing anything else.
Also, Objective C's type system is less stringently enforced than those other languages. It's quite legal (although a very bad idea) to do what you have done here, which is to declare a pointer of type NSMutableArray and then assign it to (I'm guessing) an NSDictionary.

Related

Woes in trapping valueForKeyPath: without a valid path

I have a nested NSMutableDictionary and am successfully pulling out a value at some 'depth' nested in other dictionaries, like:
NSNumber *num = [myDictionary valueForKeyPath:#"league.team.away.score"];
All is good. And I can confirm that all dictionaries at all levels are mutable.
But... what if that key path does not exist?
As expected, I get an NSUndefinedKeyException. I tried a fix with a try/catch tactic, to no avail.
Apple's solution to this concerns overriding valueForUndefinedKey:
"Subclasses can override this method to return an alternate value for undefined keys. The default implementation raises an NSUndefinedKeyException."
Great... so I create a subclass NSMutableDictionaryMod, then I get a complaint...
[NSMutableDictionary initWithCapacity:] method only defined for abstract class. Define -[NSMutableDictionaryMod initWithCapacity:]!
I go define said initializer (and can confirm it gets there in Xcode), but boom it crashes on the self = [super initWithCapacity:numItems]; line.
Then I noticed this Apple gem on NSMutableDictionary:
There should typically be little need to subclass NSMutableDictionary. If you do need to customize behavior, it is often better to consider composition rather than subclassing.
Any suggestions would be appreciated.

CoreData Object typing won't work

Can someone explain to me why this doesn't work:
CoreDataClass *classObject = (CoreDataClass *)[some method that returns a dictionary with exact KVC pairs that match CoreDataClass];
NSString *myString = classObject.stringProperty;
But this does:
CoreDataClass *classObject = (CoreDataClass *)[some method that returns a dictionary with exact KVC pairs that match CoreDataClass];
NSString *myString = [classObject valueForKey:#"stringProperty"];
EDIT:
What's the easiest way to cast the dictionary as my NSManagedObjectClass CoreDataClass so I can access properties directly?
It doesn't work since KVC compliance is not at all what defines classes or makes them castable - the class hierarchy exists for a reason, and just ensuring adherence to certain methods doesn't magically make something an instance of a completely different class. Keep in mind that the dot-accessor syntax is just sugar for a method send, so these two are equivalent:
classObject.stringProperty
[classObject stringProperty]
...and the latter obviously isn't valid for instances of NSDictionary (i.e. [[NSDictionary class] instancesRespondToSelector:#selector(stringProperty)] is NO).
Your latter example works because of the very premise of your question: if something is KVC-compliant for the key stringProperty, and you ask it for a value for that key, then obviously you get something back. Furthermore, both NSDictionary and CoreDataClass respond to the selector -valueForKey:, so the message send actually works at runtime.
The best way to get the two across isn't a "cast" at all - it's a complete conversion, at the property level, of the data involved. You might consider creating a custom -initWith... method on CoreDataClass that lets you instantiate its properties from a dictionary, or finding a way to get your method to return an actual instance of CoreDataClass instead of an NSDictionary.
Note that this solution may differ from the "easiest" way to get the data across, which is effectively to keep doing what you're doing and use -valueForKey: (though preferably without the cast, which is misleading).
Casting objects only appears to work (in the sense that you won't get type-checking errors) because it's a hint to the compiler, but it doesn't actually change anything about what the pointer points to, so you are still pointing to an NSDictionary. This is because, at the end of the day, you are essentially casting a pointer to a pointer, but telling Xcode that you are allowed to send a different set of selectors to it.
For NSManagedObjects, creation from a dictionary depends on a few things, but the recommended way is to make a class method on your custom class which will use NSEntityDescription and you NSManagedObjectContext, and sets the properties from the dictionary to the object:
+(CoreDataClass *) coreDataObjectWithDictionary:(NSDictionary *) spec {
CoreDataClass *myInstance = [NSEntityDescription insertNewObjectForEntityForName: #"CoreDataClass" inManagedObjectContext: [myMOCProvider sharedMOC];
myInstance.someProp = [spec valueForKey:#"someProp"];
}

Do I need to initialize an iOS empty nested array that's part of a plist import?

the code below is working, but I want to make sure it's correct. I'm nervous about having an empty Array inside my dictionary that I create from the plist, since typically it seems that if you don't, say, initWithCapacity:1 then you often get memory errors once you start trying to add items.
At least, that's been my experience with NSMutableDictionary. However, this is the first time I'm trying to implement nested data objects, so perhaps the reason this code works is that the nested array is automatically initialized when it's imported as part of its parent dictionary?
Any and all comments appreciated. Thanks.
First, here's what the plist looks like that I'm using to create my dictionary:
Next, here's my code where I'm using the plist to create a dictionary, then adding an item to dataArray
// Create a pointer to a dictionary
NSMutableDictionary *dictionary;
// Read "SomeData.plist" from application bundle
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"SomeData.plist"];
dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:finalPath];
// Now let's see if we can successfully add an item to the end of this empty nested array. How 'bout the number 23
NSNumber *yetAnotherNumber = [NSNumber numberWithInt:23];
[[dictionary objectForKey:#"dataArray"] addObject:yetAnotherNumber];
// Dump the contents of the dictionary to the console
NSLog(#"%#", dictionary);
Okay, fine, simple, good. When I Log the dictionary contents it shows that "23" has been added as an array value to dataArray. So the code works. But again, I want to confirm that I'm not "getting lucky" here, with my code just happening to work even though I'm not properly initializing that nested array. If so, then I could run into unanticipated errors later on.
So to sum up, dataArray is an empty array inside the .plist, so do I need to initialize it somehow (using, for example initWithCapacity: or something else) before I can properly populate it, or is the way I'm coding here just fine?
Thanks again.
EDIT
Hey all. I've been doing continued research on this, in the interests of finding a satisfying answer. I think I may have stumbled upon something, via this link on deep copying. His previous posts on deep copying had presented some code to do essentially what I was looking for above: create a mutable copy of a dictionary or array, from a plist, that also has mutable sub-structures.
However, as mentioned in the link above, it looks like these methods were superfluous, due to the CFPropertyListCreateDeepCopy method, which can be invoked with a call such as
testData = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, [NSDictionary dictionaryWithContentsOfFile:path], kCFPropertyListMutableContainersAndLeaves);
So, my question is, can I properly use CFPropertyListCreateDeepCopy, in the way shown, to achieve what I've been asking about here? In other words, can I use this method to import my dictionary from a plist with fully mutable, nested data objects?
As I mentioned in one of the comments, I know I can create a nested, mutable dictionary manually, but for complex data that's just not practical, and it seems unlikely that built-in methods to import a mutable plist don't exist. So, based on the above, it looks like I've possibly found the solution, but I'm still too new to this to be able to say for sure. Please advise.
(Side note: I would simply test the code, but as we've established, the current SDK is buggy with regard to allow you to edit immutable nested dictionaries, contrary to the documented behavior. So as before, I'm not just interested in whether this works, but whether it's correct)
Thanks in advance.
init... methods should only be called once, immediately after a call to alloc or allocWithZone:. When framework code creates and returns an object or graph of objects, their init... methods have already been called, so sending another init... message would have undefined results. Don't do that.
Interestingly, in spite of what the documentation appears to say (and admittedly I probably missed a key sentence or paragraph somewhere), when you create an instance of a mutable collection by reading a plist, any nested collections are also mutable. I ran the following little experiment in a test harness just to be sure:
NSMutableDictionary *pets = [NSMutableDictionary dictionaryWithContentsOfFile:#"/tmp/Pets.plist"];
NSMutableArray *cats = [pets objectForKey:#"cats"];
[cats addObject:#"Foo"]; // EDIT: Added line I accidentally omitted earlier
NSLog(#"%#", cats);
So again, the nested collections created when you read in the plist are fully initialized, and mutable to boot, so you can simply use them, as you've been doing.
EDIT
However, after doing some further reading of the docs, I think the OP is right to feel uneasy about relying on what is apparently an undocumented feature of the current version of the SDK. For example, the Property List Programming Guide states:
If you load the property list with
this call:
NSMutableArray * ma = [NSMutableArray arrayWithContentsOfFile:xmlFile];
ma is a mutable array with immutable
dictionaries in each element. Each key
and each value in each dictionary are
immutable.
So, to be on the safe side, if you need a nested collection to be mutable, you should create it yourself. For example, I'd recommend rewriting the code in the example above as follows:
NSMutableDictionary *pets = [NSMutableDictionary dictionaryWithContentsOfFile:#"/tmp/Pets.plist"];
NSArray *cats = [pets objectForKey:#"cats"];
NSMutableArray *mutableCats = [cats mutableCopy];
[pets setObject:mutableCats forKey:cats];
[mutableCats release];
You can then safely make changes to the nested mutable collection:
[mutableCats addObject:#"Foo"];
Any object in a dictionary which is created by reading from disk will be properly initialized. You will not have to do it on your own. However, as pointed out by jlehr, contents of the dictionary should be immutable. If you want the contents of the dictionary to be mutable, you will need to change them on your own. I have no idea why your program is not throwing an exception.
I do not know why you are getting memory errors while not using initWithCapacity:1 in other situations. The following code is perfectly valid:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:#"object1"];
[array addObject:#"object2"];
NSLog(#"%#",array);
[array release];
If you don't specify a capacity, the array won't pre-allocate any memory, but it will allocate memory as required later.
Edit:
It is perfectly acceptable to use NSDictionary with CFPropertyListCreateDeepCopy. In Core Foundation, a CFPropertyList can be a CFDictionary, CFArray, CFNumber, CFString, or CFData. Since NSDictionary is toll-free bridged to CFDictionary, you can use it wherever a CFDictionary is asked for by casting, and vice-versa. Your code as is will give a warning, but you can suppress it by casting the dictionary and return values.
NSDictionary *testData = (NSDictionary*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)[NSDictionary dictionaryWithContentsOfFile:path], kCFPropertyListMutableContainersAndLeaves);

Assigning, mutable to immutable array?

Would someone be so kind as to confirm that I am understanding this correctly. My initial response to this was that the mutableArray when assigned to an immutableArray would become an immutableArray, however this is not the case.
Is this because the initial array is allocated as a mutableArray and the immutableArray is just assigned to point to the same object. The compiler gives a warning, but the the code executes just fine at runtime.
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:#"Teddy", #"Dog", nil];
NSArray *iArray = mArray;
[iArray addObject:#"Snoss"]; // Normally this would crash NSArray
much appreciated
gary.
Don't confuse the physical object that you just created, with how you are effectivley casting it in your code.
In this case, you did physically create a NSMutableArray.
Then you effectivley cast it to an NSArray - which is totally valid - for example, there are many cases where a function might want an NSArray, and you can pass it an NSArray, or anything derived from it (like an NSMutableArray).
The problem is that you get a compiler warning because you are trying to call addObject on an object which the compiler thinks is just an NSArray because that's what it's physical type is.
This will actually work, because it actually is an NSMutableArray, and in runtime would respond to that selector.
It's bad coding practice to do it this way, because NSArray doesn't actualy respond to the addObject selector. For example, I could create a function, like:
-(void) addIt:(NSArray)myThing {
[myThing addObject:#"New String"];
}
The compiler would give you a warning saying the "NSArray" doesn't respond to the "addObject" selector. If you cast an NSMutableArray, and passed it to this function, it myThing, it would actually work.
It's bad practice though, because if you actually passed an NSArray it would crash.
Summary: Don't confuse what the object really is vs. what you are making the compiler interpret it as.
Yes, you are right. You should do
NSArray *array = [NSArray arrayWithArray:mutableArray];
to ensure that nobody can change the array
Your iArray is actually referencing to NSMutableArray instance, that's why it is a NSMutableArray.
Obj-c doesn't have strict check on class types, all objects are of type 'id'.
You could write
NSNumber *iArray = mArray
Compiler will show a warning of wrong cast (not error). But it will work.
Don't mess with pointers, there is no object type transformations as you can expect in C++. (there are overloadable operators for casting object to another class).
Obj-c works with objects much like script/interpreted languages. Dynamic typing (objects only), reflection, dynamic change of methods of instance of classes etc - full flexibility. A perfect mix of speed of low-level C/C++ and flexibility of dynamism.
AFAIK, you are correct. NSMutableArray is a subclass of NSArray, so you can assign mArray to iArray here without a problem.
However, it isn't clean code and you should avoid it.

Should I verify objects inside Foundation API containers?

In languages like C++ and C# when you create a contain such as a std::vector or a C# list you explicitly declare the container type when you create it:
C++:
std::vector<MyObject>
C#:
List<MyObject> list = new List<MyObject>();
Looking at the code above, I know immediately that these containers can only contain objects of type MyObject and the compiler will complain if I try to add an object that isn't off this type.
Since Objective-C is a dynamic language, we don't have the privilege of the compiler warning us about this (because it is a perfectly valid but potentially dangerous thing to do):
Objective-C:
NSDictionary *dict = [[NSDictionary alloc]init];
[dict setValue:[[SomeClass alloc]init] forKey:#"someClass"];
[dict setValue:[[NSMutableString alloc]init] forKey:#"mutableString"];
BOOL classIsSomeClass = [[dict objectForKey:#"someClass"] isKindOfClass:[SomeClass class]];
Instead something like an NSDictionary or NSArray will store and accept objects of any type that inherits from NSObject. I find this in itself very flexible but I cannot really be sure of the object type in the container I can only really know at runtime whereas with c++ or c# I know this at compile time and just by looking at the code.
Should I be validating the contents of the containers when adding, using and removing objects for container classes (NSArray, NSSet, NSDictionary, etc) from Apple's Foundation Framework? Or is this okay in all circumstances and will verification hurt performance much?:
NSDictionary *dict = [[NSDictionary alloc]init];
[dict objectForKey:#"someKey"]; // return nil?
Objective-C's dynamic messaging is much more like dynamic languages such as Python or Ruby. In these languages, the standard paradigm is often known as "duck typing". In other words, if an object instance quacks like a duck (i.e. responds to the message you're sending), it's a duck. In Objective-C, methods can be added at run time by a number of mechanisms, outside of the object inheritance hierarchy. So, it's much more common to ask whether an instance responds to a particular selector:
if([obj respondsToSelector:#selector(myMethod)]) {
[obj myMethod];
}
than to ask whether obj belongs to a certain class' hierarchy.
For the most part, Objective-C developers don't do this check unless they're getting object instances from "unknown" modules. Instead, we rely heavily on compiler warnings (the Objective-C compiler will warn about sending a message to a type that it isn't sure can receive that message) and unit testing. In this case, unit test to confirm that the correct objects are going into the collection and that you get the expected types out of the collection would probably go a long way to allaying your fears.
It does seem to be the "Objective-C Way" to avoid checking the types of an object taken from a collection. It's of course debatable whether this is good, but I think it's part of a general theme of preferring to think about the messages an object responds to rather than the object itself.
An example of this is the various ...Value (e.g. stringValue, intValue, etc.) messages that many objects respond to. Also worth noting is the fact that the id type automatically suppresses any warnings of the so-and-so may not respond to the such-and-such message variety.
I would say the pattern in Objective-C is to only store objects of one type in a container - and pretty much always you are sure of what is going into a container. That's why very few people in practice actually take the time to check the contents of a collection. When I do want to verify something, I usually use isKindOfClass: and a properly typed object to hold an item from the collection.
If you are really concerned about typing for some reason it would be pretty easy to create a wrapper class that implemented typed versions of objectAtIndex: and other common NSArray methods - note I'm not talking about a subclass of NSArray or any other collection, just an object that had similar message names. That kind of thing can be a drop in for lots of uses and you could always add a fall through method to get to the backing collection. But I think it's more trouble than it is worth and moves away from gully embracing the language.
In practice over many, many applications I almost never see "wrong type of object in an array" come up as an issue.
Now for a method that accepts an argument of typeID, that I am a lot more likely to check the type of before use - because those methods tend to take in a much wider range of objects.