Monkey-Patching iOS with Objective-C Categories Part III: Swizzling

Have you ever wanted to introduce new functionality to base classes in the iOS SDK?
Or just make them work a little differently? In order to do so, you must enter the wild and dangerous world of monkey-patching.

In this series of posts, we’ll show how to monkey-patch in Objective-C through categories to add and change methods, to add new instance variables and properties, and introduce swizzling, a technique that allows us to extend and preserve existing functionality. TL;DR »

In the first post we showed how you can add or override methods with extensions. The second post focused on getting around restrictions on creating instance variables when adding properties to the classes being modified. This third and final post covers the technique of swizzling to override existing methods while preserving their behaviour.

The Problem with Simply Overriding

Back in the original post of this series we discussed that while categories certainly allowed us to bluntly override existing methods of any class in the iOS SDK this was strongly discouraged for two reasons:

  1. Other frameworks, both in the SDK or third-party, rely on the expected behavior of the original method. We would either have to re-implement that behavior in addition to the new functionality we wanted to introduce, or suffer side effects or more major errors due to its abscence.
  2. If multiple categories override the same method only the last one loaded wins out as load order is non-deterministic; its possible even the original method will remain unaffected.

Ideally we need a way to have the new method handle calls to the original mathod, but preserve the original method so as to be able to invoke it when needed, much like the Ruby on Rails’ alias_method_chain. Which is exactly what the swizzling technique provides.

The Solution: Swizzling

So far I’ve been incorrectly referring to the “methods” of a class. But in Objective-C, when you write the following:

[self presentViewController:mailController animated:YES completion:nil];

you are not actually invoking the presentViewController:animated:completion: method but are instead sending a presentViewController:animated:completion: message! How an object handles that message is determined at run-time by looking for a method under the message identifier or as it is commonly known as the selector. Normally this is the signature the method was declared under but it can be changed at run-time!

Swizzling is simply exchanging the implementation of two of a class’ methods so that when a message is sent using the original selector of one it actually goes to the other. In general, whether for monkey-patching or other scenarios, this is accomplished by using a number of Objective-C Runtime functions:

Walking through the above code:

  1. First we build selectors (the SEL variables) to identify the methods we are swizzling; in this case firstMethod and secondMethod.
  2. References to the methods the selectors point to (represented by the Method data type) are then retrieved.
  3. We first attempt to add the implementation of the second method under the selector of the first method. We do this in case the first method doesn’t truly exist, which is sometimes a possibility.
  4. If the method was added successfully we need SOMETHING under the selector of the second method, so we simply replace it with the first method’s (empty) implementation.
  5. If we failed to add the method, the first method already exists, so we can simply exchange their implementations.

Now, for the purposes of “monkey-patching”, we rarely want to exchange two existing methods. Instead we introduce a new method and then swizzle it with the original. Any calls to the original method will now be directed to the new implementation while the original implementation can be invoked under the name of the new method!

Let’s look at…

An Example

Going back to the last post’s scenario of extending UIViewController with tour-guide functionality, suppose we want the tour guide information is to appear the first time a view is displayed to a user. The ideal place to have this happen is as part of the viewWillAppear: call all controllers receive. Remember, we could spend time adding a sub-class for every controller variation we will use, but that could lead to unnecessary code bloat. But since viewWillAppear: is critical to the UI life-cycle, we can’t simply replace it. Hence, we need to swizzle it!

As a best practice when we swizzle a method, it’s with a method with the same signature and a similar but unique name. In our case, we’ll be creating tourGuideWillAppear:

Note the call to tourGuideWillAppear within its own implementation. You may be asking yourself “Isn’t that going to result in an infinite recursive loop?”

But at what you have to remember is that at the point the method is invoked the swizzling will have already taken place. That seemingly recursive call will actually go to the original viewWillAppear:. So remember, to invoke the original method implmentation, call it with the new method’s name.

Swizzle on Load

Of course, we still have to at some point perform the swizzle. The first instinct would be to toss it into the init method of a class, but this is incorrect because:

  1. We are not creating a class, but a category that will be mixed into a class whose init you probably don’t want to override and
  2. Even if that was possible, it’s something you only want to do once per class, and not in the per instance constructor!

Luckily, when the Objective-C Runtime loads a category, it invokes a class-level load method. This is the perfect opportunity to perform the swizzle. We also wrap it with a dispatch_once block call to ensure it only happens the one time:

And with that our swizzle is complete; when the framework calls viewWillAppear: on any controller it will pass through our tourGuideWillAppear:, triggering our custom tour-guide functionality. We can apply this same technique to extend any class method whether called by the framework or us directly, injecting new behavior while preserving any critical functionality.

We have achieved true monkey-patching!

DRYing it Up

Our example has us replacing one method in one class but already it makes for a lot of code. Imagine having to repeat that multiple times across many categories. Let us DRY it up by introducing, in an elegantly meta way, a swizzling category on the base NSObject:

Now in the +load of any category we simply call swizzleInstanceSelector on the category itself with the selectors of the methods we are swizzling. Here’s the final UIViewController+TourGuide category implementation to illustrate that and all the other monkey-patching techniques we have learned in this series:

TL;DR

  • Use swizzling to preserve the original method behavior instead of simply overriding to avoid side-effects.
  • Swizzling is simply the exchange of the identifiers of two methods so they point to each other’s implementations.
  • After swizzling you invoke the original method’s implementation but calling the new implementations identifier.
  • Swizzle in the +load method of your category.

About Rudy Jahchan

Rudy’s fascination with computer programming began at age 10 when he mistakenly picked up the Micro-Adventure book Space Attack; he thought it was going to be about Star Wars. That happy accident led him to graduate from McGill University in Computer Science and start a 12 year career in software development playing with a wide range of technology; everything from web applications to cryptology to NoSQL.
This entry was posted in Mobile. Bookmark the permalink.

11 Responses to Monkey-Patching iOS with Objective-C Categories Part III: Swizzling

  1. Dude! what a snatch! one awesome swizzling right there!

    I wonder what would really happen when you subclass/categorize down the road and you totally forget you’ve *swizzled*… especailly when you have no choice left after monkey patching the whole UIViewController at load!

    I believe your demo of how to swizzle is really awesome. On the other hand, if you had mentioned what side-effect/downside were there to catch, y’know just for the sake of caveat-emptor, it would have totally rocked!

    • Rudy Jahchan says:

      Thanks for the compliments.

      And yes, being aware of what you have swizzled or added in general through the various techniques I discussed is a concern. In the first blog post of this series I suggest gathering all your extensions in a single header file so your team knowns what is being added in.

      The side-effects can be wide and varied only because, well, what someone is doing when they patch is wide and varied. For the most part though it boils down to the problems I outlined at the top of this post; if you’re changing the expected behavior of core functionality, it may cause unexpected consequences. It’s best to use this for “hooking” into behavior, adding aspects vs overriding it.

      • I am not against the idea of swizzling. In fact, sometimes, that might be the only option in your hands. (e.g. disabling google map from iOS5). Further, I love how the SO page compares swizzling with sharp knives. It is true that sharp knives are much safer if you know what you have in hand.

        My point was your example promoted swizzling of core function from the get-go; Just one UIViewController instance receives a message, then the hook kicks in. Hmm… Mike Ash long time ago published how to implement runtime swizzling in his blog and I loved it except how I was ripped off as soon as I followed.

        I think that you can find much better ways to balance between risk & benefit from swizzling with the almighty obj-c runtime.

        It was my bad I did not went to read your first post. My apologies.
        Hey, thanks for the SO article btw. ;)

    • Rudy Jahchan says:

      Also; here is an excellent stackoverflow post someone posted on Reddit in reply to my post. It does a great job proposing an alternative swizzling technique, as well as the inherent dangers in swizzling:

      http://stackoverflow.com/questions/5339276/what-are-the-dangers-of-method-swizzling-in-objective-c

      Cheers.

    • JonahWilliams says:

      I think Rudy demonstrated a good guideline for what sort of behaviors are reasonable to swizzle onto a class but he did not explicitly call it out; swizzled methods should add side effects to the original implementation without changing the original behavior.

      Adding a hook which does not change the original method’s behavior like the “tour” example here or adding logging around methods should be safe operations. Mutating method parameters or return values would not be safe and should be avoided or at least very carefully considered. Really this is just applying the Liskov substitution principle (consider the swizzled version of the class as a subtype of the original class).

  2. Dude says:

    this tutorial is awesome! I’m curious why you pass [self class] to class_addMethod and class_replaceMethod in + (void)load while self is class itself and afaik [self class] just returns itself in class method.

  3. emice says:

    This reminds me of manipulating interrupt tables back in the DOS days. For each hardware interrupt/IRQ there would be a function pointer. You’d replace this with your function but save the original function’s pointer. Then you’d terminate and stay resident in memory, waiting for the OS to call your function after a key was pressed, or some serial port bytes received, etc.

  4. Guest says:

    Thanks for this post. Does swizzling a method in a Framework ( like say NSURL ) have effect system wide or only in the app in which it was swizzled ?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>