views:

57

answers:

3

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?
+5  A: 

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.

Barry Wark
i've heard the `duck typing` description before, but your explanation clarified it for me. thanks
Brock Woolf
In general, though, if you are using `respondsToSelector:` outside of very limited patterns (delegation, for example) or you are using `isKindOfClass:`, then your application design is well outside the norm of Objective-C.
bbum
@bbum: What about multithreading in Obj-C? Calling `respondsToSelector:` before you do `[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:)` seems like a good idea to me.
Brock Woolf
@Brock I'd rather my code crash than mysteriously not do some action silently. If you really want to go that route, better to do `if (! [o respondsToSelector: @selector(myThreadMainMethod:)]) abort();`
bbum
@bbum: I agree, but it's a tradeoff. I always stick at least a `DebugLog()` to notify myself of a condition that is not working as expected. I would rather that my app had a small bug rather than crash for my users. Maybe that's the wrong approach but I think my users would rather it didn't crash. Opinions are welcome.
Brock Woolf
+2  A: 

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.

David
+1 for "think about messages rather than the object itself"
Brock Woolf
A: 

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.

Kendall Helmstetter Gelner