One of my guilty pleasures is hanging out on Mac programming IRC channels (#macsb and #macdev on irc.freenode.net) Occasionally an interesting discussion erupts over some esoteric topic, but for the most part it’s ordinary questions about the basics of Cocoa programming.
One misconception about Objective-C that comes up occasionally is the assumption that Objective-C does more than it does. In particular things like
“I compare two strings/objects/dictionaries with ==
, and the test fails. Cocoa is broken!” (quickly followed by the Mac sucks, Xcode blows goats, and so on)
or
“Can I concatenate two strings by using +
?” (quickly followed by the Mac sucks, Xcode blows goats, ObjC2 should have gotten operator overloading, and so on)
Objective-C is a pretty old language, predating Java, Perl, Pythong, Ruby, and many of the modern features of C++. It is just C with a couple of extra knobs on it, with very little extra behavior added to the language. All of the magic is in the runtime which does message dispatching. There is no operator overloading in the language. Pretty much all the language lets you do is interact with integer and floating point values, and calculate addresses via pointers and offsets. In C and Objective-C, there is no implicit complex behavior hiding behind the operators.
Now, folks who come from VB (I presume), Java, scripting languages, and C++ expect things like the ==
operator to be an equality operator, knowing the contents of the objects being compared; or expect the +
operator to be a concatenating operator, dynamically creating objects.
Think about what happens under the hood: comparing two strings compares each of the characters in the string (looping over the contents), or comparing two dictionaries would compare each of the elements in the dictionary (looping over the keys). Likewise, to concatenate two strings, you’ll probably be looping over the two, or allocating blocks and using a memcpy()
-like operation. (and in general, using concatenation to build strings can bite you when you go to internationalize your application)
C and Objective-C don’t do anything that convenient. All ==
does is compare a small number of bytes for an address when dealing with pointers or an integer/float value. And when you come down to it, those are the same operation. In particular, no looping. All +
does is add two values. In particular, no allocations.
So, when you’re dealing with Objective-C, and things don’t behave the way you think they should, consider what’s going on under the hood:
NSString *thing1 = ...;
NSString *thing2 = ...;
if (thing1 == thing2) {
This is just pure C right here (no @
‘s or []
‘s are involved, so the Objective-C part of the world doesn’t kick in. No ()
‘s involved, so no function calls are happening. Therefore, only a small number of bytes are involved. Each string could be megabytes long, but the comparison will just be four bytes (eight bytes on a 64-bit system). So this is just testing whether one address is equal to another address. An “identity test”, vs a test for equivalency, which is what the programmer probably wanted in the first place. Assuming that the two strings are not pointing to the same @"literal NSString"
, these will have different addresses, and the comparison will be a false one. (it’s not my nose, it’s a false one)
Now this code:
if ([thing1 isEqualToString: thing2]) {
Does do an equivalency test. Since you’re actually invoking a method here, and methods are arbitrary code, this has the opportunity to loop through the strings and compare them character by character.
So the moral of this poorly-organized story? C is dumb about data. It only does a few blindingly simple things. Objective-C is a very thin layer on top of C, and adds nothing to the existing C syntax. If you’re getting unexpected results with the built-in operators, you’re probably assuming the base language is doing more than it should, and you should update your expectations appropriately.
And while I’m thinking about it, I’m quite glad Objective-C does not have operator overloading. The gotchas are just too subtle, and I don’t mind trading some extra typing or an explicit function or method call in exchange for not having to track down subtle changes in behavior when some class I’m using decides to overload an operator. Hint: in C++, how does operator&&
change behavior when it’s overridden, vs the compiler default? You’d be surprised how many experienced C++ developers don’t know that bit of trivia.
If you really want operator overloading (it does make sense in some situations, like the canonical matrix libraries everyone seems to be writing, or at least using for examples of why overloading is a Good Thing), there’s always Objective-C++.