One of the strengths of Objective-C is that it allows the separation of interface and implementation. You can see this capability most obviously when comparing how Objective-C and C++ programs deal with strings. In C++, strings are represented by std::string, a class with some serious limitations. As a result, large C++ libraries typically include their own string classes. Programmers using different libraries then have to convert between them by going via std::string or C strings, which involves a lot of redundant copying.
In Objective-C, NSString is a class cluster, and you can create concrete subclasses trivially by implementing just a small number of methods. I most recently did this for interacting with the International Components for Unicode (ICU) library. ICU uses its own internal string-abstract data type, implemented in C with functions for getting ranges of characters and so on. I implemented a pair of wrappers, allowing NSStrings to be exposed as ICU strings, or created wrapping ICU strings. This meant that my code working with the ICU library never had to copy strings when moving them between the two libraries.
This approach can be very helpful in other places. NeXT used it to great effect with the various collection classes. These are all class clusters, with different subclasses optimized for different contents. If you're using a single class with some data that's causing poor performance, consider writing a custom subclass for that particular case. This trick is especially useful if you need to reduce memory usage somewhat. Lots of classes in Cocoa have subclasses with a smaller set of instance variables than their interfaces would suggest.