Introduction to Objective-C for Programmers, part III

Introduction to Objective-C for Programmers, part III

Introduction to Objective-C for Programmers contains the following parts already:

This is the third part of the series ‘Introduction to Objective-C for Programmers’. This post will be about creazy Objective-C feature called method swizzling.

Method Swizzling

Method Swizzling is an ability to ‘patch’ methods, which can come usefull with methods where you have no access to the code (eg. 3rd party libraries). This practice is considered dangerous and you should be perfectly sure what you are doing while using method swizzling in Objective-C.
Consider the following example:
We have an NSString for which we want to override the standard ‘description’ method to return to us the description but prefixed with the value ‘softwarepassion says: ‘.
For this to work we need to do the following:

Create Objective-C Class Category

As we are going to implement our own version of ‘description’ method from NSString class we will create a new category on NSString, lets call it ‘StringPrefixing’.
Go to Xcode, and create a new category on NSString class with the name mentioned above, you should end up with two files called:

NSString+StringPrefixing.h as well as NSString+StringPrefixing.m

Once you have that ready, add a method with will pretend to be our original description method, I’ll call it ‘my_description’.
So in NSString+StringPrefixing.h we have the following code:

@interface NSString (StringPrefixing)
- (NSString *)my_description;
@end

Override load() method on you class and add the swizzling logic there

Inside our category implementation we add the logic to our own description method and perform the swizzling itself:

#import "NSString+StringPrefixing.h"
#import <objc/runtime.h>;

@implementation NSString (StringPrefixing)

+ (void)load {
Method originalMethod = class_getInstanceMethod([NSString class], @selector(description));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(my_description));
method_exchangeImplementations(originalMethod, swappedMethod);
}

- (NSString *)my_description{
NSString *original = [self my_description];
NSString *prefix = @"softwarepassion: ";
NSString *result = [prefix stringByAppendingString:original];
return result;
}
@end

Remember to import <objc/runtime.h>

To use it, just add an import on your NSString+StringPrefixing.h header file.

If you wander about calling my_description from inside ‘my_description’ method you are not alone, the trick is that ‘my_description called on ‘self’ at that point will be swizzled already so its not a recursive call as you probably thought.

From now on you can use NSString as normal, eg:

NSString *string = @"foo";
NSLog(@"swizzled NSString: %@",string);

Will print :

swizzled NSString: softwarepassion: foo
One important point to mention here is that you should always try to do your swizzling inside ‘load’ method, this way you will escape the dangers of the fact that method swizzling is not atomic.

There are some other pitfals to consider when swizzling, I do recommend reading great SO answer on the topic found here: http://stackoverflow.com/questions/5339276/what-are-the-dangers-of-method-swizzling-in-objective-c

To sum up, possible pitfals of method swizzling are as follows:

  • Method swizzling is not atomic
  • Changes behavior of un-owned code
  • Possible naming conflicts
  • Swizzling changes the method’s arguments
  • The order of swizzles matters
  • Difficult to understand (looks recursive)
  • Difficult to debug

Maybe it is dangerous and difficult to use but I found it to be a really interesting feature and I wonder what are good use cases for such a functionality except the debugging.

 

Introduction to Objective-C for Programmers contains the following parts already:

  • Part I – literals
  • Part II – class cluster patter, categories, description method
  • Part III – method swizzling
  • Part IV – error model, delegates

 

Leave a Reply

Related Posts

    No Releated Posts