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.
Related
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"];
}
I'm writing an objective-C game and I'm at the stage where i should start optimising some of my code in the game loops.
I have an extensive amount of class compare methods used,
if ([obj isMemberOfClass:[SomeClass class]])
etc.
I heard this sort of checking is quite expensive, because I choose only to have 1 array populated with multiple classes, I need some sort of class check.
I was thinking perhaps that adding a property to NSObject subclassing NSObject to contain a string property, that during initialisation i would make equal to the class name of that particular subclass. Then simply doing a
if ([obj.klass isEqualTo:#"SomeClass"])
Would this be beneficial?
I'm trying to keep as much dynamic coding out of the game loops as possible.
Thanks!
Short answer: no. String comparison is prohibitively more expensive compared to other methods of comparing (or: classifying, categorizing) objects.
Long answer: don't optimize what you haven't analyzed, measured and compared. What you really want to do before you start optimizing is to get a clear picture of how your app behaves and what its performance bottlenecks are. The change you're attempting is unlikely to lead to any noticeable change in performance, so I suggest to first find the real bottlenecks.
In this particular case, sending isEqual to an NSString is 4 times slower than isMemberOfClass if the test fails. And such conditional tests fail most of the time, which is why you should ignore the results of the test succeeding.
The successful string comparison is fast because it's a simple pointer comparison, if two strings are equal it is likely that they point to the same memory address. If they're not equal, then each character in the string will be compared for equality, probably by using the hash method.
Here are the results of the Object Comparison tests that I added to my performance test project. You can use that project to make further tests.
This is not really a direct answer to your question but is an answer in a broader sense.
In Objective-C the philosophy is more like that of Smalltalk in which you send the message and let the object decide what to do with it. If you find yourself having to do lots of tests to see what class an object is, you need to rethink your design.
For instance, if you have an array of objects and you want to convert each one to an integer to do some maths on it, you can do something like this:
for (id anObj in array)
{
int anInt = [anObj intValue];
// do something with anInt
}
It doesn't matter what the class of each anObj is, you can send -intValue to it. If your array is full of NSStrings and NSNumbers, for example, it doesn't matter, the code above will do what you expect.
Many classes do not define a method for the selector -intValue. For example, if you send that message to an instance of NSData it will respond by throwing an exception. There are a couple of ways to resolve this depending on circumstances.
ignore objects that don't respond to the selector by testing to see if the object knows about the selector
for (id anObj in array)
{
if ([anObject respondsToSelector: #selector(intValue)])
{
int anInt = [anObj intValue];
// do something with anInt
}
}
Define the selector for all classes you know will be put in the array. This is done by declaring a category. This way you can extend any Objective-C class without subclassing. For instance, you can define an intValue method for NSData that returns its length, or the sum of its bytes or some other appropriate value.
Formalise the requirement by declaring a protocol. You can then test for conformance to the protocol, or rely on compile time checks to make sure the objects you put in the array conform to the protocol.
There are lots of things you can do, but you need to get away a bit from the C++/Java model of class hierarchies. Objective-C is much more flexible in that respect.
I bring this up because objects that compare the same with isEquals: aren't necessarily identical. Some (many) objects only compare certain properties to determine equality.
That makes the exact behavior of the following NSSet methods important:
setByAddingObject:
setByAddingObjectsFromArray:
setByAddingObjectsFromSet:
The documentation does not specify what happens when the receiver and the parameter contain equivalent objects. Will the resulting NSSet contain the object from the receiver, or the object from the "other" parameter?
Note that in NSMutableSet, it DOES specify the behavior of its add methods--objects will NOT be added if an "equal" object already exists in the set.
The documentation of NSMutableSet's addObject: method used to cover a similar case:
If anObject is already present in the set, this method has no effect on either the set or anObject.
But, as you can see from following the link, the current version doesn't even say that. And even that statement really only covers trying to add the same object; it does not specifically address adding a different but equal object.
Relying on observed but not documented behavior is dangerous, not just because it can change between OS versions, but also because it can change within the very same process. That's because NSSet is a class cluster, meaning there may be multiple implementations. Which one you get depends on how you create the set; there is no way to ensure that a specific implementation will be chosen or even exist.*
That's because it shouldn't matter. Every one of the clustered subclasses presents the same behavior as defined in the cluster's interface and documentation. (If it ever doesn't, that's a bug and you should report it.) Given that all the subclasses do effectively the same things, it shouldn't matter which one you get an instance of.
The same principle applies to your objects. They're equal! For that reason, it shouldn't matter which one is in the set. If it does matter, then they are not truly equal, and you need to make the objects' definition of equality more rigid. (Don't forget to update both isEqual: and hash.)
Depending on what you're using the set for, you may want to take that even farther and ensure that no two equal objects can exist. To do this, move the ownership, maintenance, and use of the set into the member objects' class, and have it always return a matching object instead of creating a new one whenever possible and appropriate.
*And even if you could choose one of the implementations, there's no guarantee that it'd have the behavior you observed forever—it could, and Murphy says probably will, be different in another OS version.
I tested this with the following code. SomeClass is defined such that propertyA is the only property considered in hash and isEquals:
SomeClass *objectA = [[[SomeClass alloc] init] autorelease];
objectA.propertyA = #"test";
objectA.propertyB = #"objectA";
SomeClass *objectB = [[[SomeClass alloc] init] autorelease];
objectB.propertyA = #"test";
objectB.propertyB = #"objectB";
NSSet *setA = [NSSet setWithObject:objectA];
NSSet *setB = [NSSet setWithObject:objectB];
NSSet *setC = [setA setByAddingObjectsFromSet:setB];
NSLog(#"Set A\n%#", [setA description]);
NSLog(#"Set B\n%#", [setB description]);
NSLog(#"Set C\n%#", [setC description]);
The output when running this code is:
2011-03-03 16:35:15.041 temp[50311:207] Set A
{(
{SomeClass propertyA:test propertyB:objectA}
)}
2011-03-03 16:35:15.041 temp[50311:207] Set B
{(
{SomeClass propertyA:test propertyB:objectB}
)}
2011-03-03 16:35:15.042 temp[50311:207] Set C
{(
{SomeClass propertyA:test propertyB:objectA}
)}
This demonstrates that the newly created NSSet will contain objects from the RECEIVER in the case that the parameter contains equivalent objects.
EDIT - I'm marking this as the answer, because it directly answers the question at hand. I would however, point out Peter's answer below and the concerns he voices. This behavior is undocumented and as such, while it's extremely unlikely these core classes will change in this regard, it's worth pointing out that risk. If you write code assuming this behavior it is possible that it will break in a future release. Caveat emptor.
I am little bit backward in knowledge on these three topics: NSMutableDictionary, NSEnumerator, NSMutableSet. When I want to use them, it feels very hard, even when I've gone through the developer documentation.
Is there any sample code to understand it clearly for all three topics?
Please help me.
Thank you,
Madan Mohan.
The best way to understand these depends on what your prior experience is. NSDictionary is exactly what it sounds like: a dictionary. What that means is that given a key (or a headword, as in a dictionary), you can look up a value (or definition):
For instance, this dictionary gives information about my dog:
KEY VALUE
-------------------------------------------
#"name" #"tucker"
#"species" #"canis lupus familiaris"
#"favorite" #"biscuits"
With a dictionary dogInfo containing that information, we could send [dogInfo objectForKey:#"name"] and expect to receive #"tucker".
The difference between NSDictionary and NSMutableDictionary is that the latter allows changes after initialization. This lets you do things like [dogInfo setObject:#"fetch" forKey:#"game"]. This is helpful for maintaining state, memoizing referentially transparent requests, etc.
NSSet is a way to have a bunch of objects, with a few important bits: there is no defined order to those objects, and there can only be one of each object (no duplicates). Use NSSet for when you need to contain unique, unordered objects. NSMutableSet is the variant of NSSet which allows for changes (such as adding or removing objects) after initialization.
NSEnumerator is a bit more complicated, but you usually won't need to deal with it unless you are writing your own libraries, are coding archaically, or are doing complex enumerations. Subclasses of NSEnumerator are used by collection classes, such as NSDictionary, NSArray, and NSSet, to allow their objects to be enumerated. Usually, you'd just enumerate over them using a foreach-loop, since they all implement <NSFastEnumeration>. But sometimes, you'll want to do more specific things, like enumerate over the objects (instead of the keys) of a dictionary, or enumerate over an array in reverse. This is where instances of NSEnumerator (usually defined as properties on your collection objects) will become helpful.
Update
Justin in the comments pointed out that NSEnumerator conforms to <NSFastEnumeration>; that means, the chances are next-to-nothing that you'll need to know how to use an NSEnumerator; you can just do a foreach loop over the enumerator itself, like so:
for (id object in [dogDict objectEnumerator]) {
// doing something with the object, disregarding its key
}
I have an NSArray that contains two types of objects. Lets call them Apple and Orange.
Apple *myApple = [self.searchQueryResults objectAtIndex:indexPath.row];
When I am building my cell's I don't know what type is in my array, apples or oranges. How can I use the generic id type to store the object, and then cast appropriately?
You can use isKindOfClass:(Class)aClass to test the class type:
if ([myObject isKindOfClass:[Apple class]])
// do stuff
I'm not sure why you think you have to cast though. NSArray doesn't care what type of object you store in it, so when you pull it out, it'll still be the same object. But if you want you could still cast by doing something like
Apple *myApple = (Apple *)myObject;
Anyways, knowing the type of class it is should be enough for you to take appropriate action, since it's my understanding that you're showing both types in the same table, all you really need to do is appropriately display their different properties.
Don't mix types of classes in an NSArray.
You can do it, you can run checks for the type - but it's a really bad idea, UNLESS they are both part of the same subclass tree (say, both derivatives of Fruit). Then at least something looking in there can assume what kind of Fruit it might be, and check for particulars.
Basically you will save yourself a lot of headaches if you don't mix types in container classes, when somewhere down the road some bit of code figures for some reason there are only Apples in there but someone throws in a Pear. Then, BOOM!