A lot of Rubyists still think Objective-C is the Devil’s language, full of compiler errors and terrifying memory management and other such infernal nonsense. And they’d be right. But what many of them don’t realize is that Objective-C is like your friend’s sexy sister – it’s got all kinds of hot moves, even if it is a little off-putting and the family resemblance a bit unsettling.
I’ll do my best to Enumerate (ha!) the dynamic similarities between the languages. If I’ve missed something, let me know!
Why yes you can. Simply call performSelector with up to two extra parameters:
[obj performSelector:@selector(myMethod) withObject:myParameter];
[obj performSelector:@selector(myMethod) withObject:myParameter withObject:anotherParameter];
You can also get a little funky and mess around with threads and delays. These are especially useful if you want to keep your UI updating (such as updating a progress bar while searching for data). Simply use the following:
Wouldn’t you know it, Objective-C has nearly the same method:
if ([obj respondsToSelector:dubiousSelector])
[obj performSelector:dubiousSelector];
Don’t worry – Cocoa provides many convenience functions, including NSSelectorFromString, which will allow you to create messages willy-nilly:
[obj performSelector:willyNillySelector];
Thanks to Ruby and Objective-C’s shared ancestry of the much-lamented SmallTalk language, you can accomplish dynamic message processing in either. It’s a bit trickier in Objective-C (specifically Cocoa), but it can be done by defining forwardInvocation.
And wouldn’t you like to know how it works? I would too! But I need to read a little more about it! So stay tuned, ‘cuz just like the subject matter, this blog is Dynamic! Ha! Get it!
No problem – the Objective-C runtime has all kinds of fanciness for messing with class and object innards. Although you can’t yet create closures at a whim, you can still switch around implementations.
Note: you should always import the following header when utilizing runtime functions:
Some functions you might be interested in for messing around with methods are:
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
IMP class_getMethodImplementation(Class cls, SEL name)
They each require some or all following information:
- The class being modified. To modify instances of the class, you’d pass
in; to modify the class itself, you’d pass[MyClass class][MyClass class]->isa
- The selector (method name) being updated
- A Method Implementation (type ‘IMP’), which is just the compiled code that underlies a given method
- A string (technically a C character array, so don’t prepend an @ symbol to it) containing the types of the method signature. The first character represents the return type (”v” for void, “@” for object references, etc.; those are the most common you’d use, but there’s a list of type encodings you can refer to for more). The second and third should always be “@:”, representing an internal necessity. Any other characters are used to represent the actual argument types.
You probably figured out that the easiest way to define a method implementation (IMP) is to use class_getMethodImplementation. That way, you can do something like the following:
class_replaceMethod([OtherClass class], @selector(otherMethodWithArg:andArg), myMethodImplementation, "v@:@@");
If that seems a little complex, don’t worry – it’s easy enough to boil it down into a convenience method:
void SwizzleMethod(Class destClass, SEL destSelector, Class origClass, SEL origSelector, BOOL isInstanceMethod) {
Method dMethod = class_getClassMethod(isInstanceMethod ? destClass : destClass->isa, destSelector);
Method oMethod = class_getClassMethod(isInstanceMethod ? origClass : origClass->isa, origSelector);
if(!class_addMethod(isInstanceMethod ? destClass : destClass->isa, destSelector, method_getImplementation(oMethod), method_getTypeEncoding(oMethod)))
method_exchangeImplementations(dMethod, oMethod);
}
// Example instance method usage:
SwizzleMethod([NSString class], @selector(length), [MyClass class], @selector(customLength), YES);
// Example class method usage:
SwizzleMethod([NSString class], @selector(string), [MyClass class], @selector(customCreator), NO);
You can put the SwizzleMethod function into a file and include it globally in your prefix header (usually named something like MyProject_Prefix.pch).
First of all, be sure to check out the Objective Support project, which accomplishes a lot of the goodness you’re used to from ActiveSupport.
Objective Support does its magic largely through the use of categories, which are a nice mixin-style feature of Objective-C. Essentially, categories allow you to “re-open” classes just like Ruby; you can create multiple categories for any given class and insert methods as you see fit. You can’t add instance variables, but you can override existing methods.
Thankfully, the implementation of categories is fairly simple:
re-opening NSString and calling our category "Utils" */
@interface NSString (Utils)
- (NSArray*) split;
- (NSArray*) split:(NSString*)delimiter;
@end
// Implement the category's interface
@implementation NSString (Utils)
- (NSArray*) split {
return [self split:@","];
}
- (NSArray*) split:(NSString*)delimiter {
return [self componentsSeparatedByString:delimiter];
}
@end
Note: you may want to include your category header in your prefix header (or at least the classes in which its methods get called), as otherwise you’ll see a bunch of “object may not respond to xxxx” warnings.
This is by no means an exhaustive list of Objective-C’s dynamism, but it covers a lot of the goodies. If there’s something I left off that you earnestly believe should be here, or you see a (gasp!) code error, let me know!







