Objective-C Runtime — Method Swizzling
is a technique for changing the actual implementation of a selector at runtime. Using this technique, we can modify the implementation of a method by changing the function pointer associated with a selector in the class's dispatch table. Basic Usage Adding a method to NSArray: Running the test: Output: More Advanced Use For example, suppose we want to track how many times each is presented to the user. We could add tracking code to in every view controller, but that's tedious and results in duplicated code. Creating subclasses is another option, but we'd need to subclass , , , and every other UIKit view controller, which also generates a lot of redundant code. This is where comes in, as shown below: Here, we used to redirect the function pointer for on UIViewController so that it now points to our custom implementation. As a result, whenever UIViewController or any of its subclasses calls , a log message will be printed. The example above nicely demonstrates how to inject new behavior into a class using . There are many other scenarios where is useful, but rather than listing more examples here, let's talk about some important considerations when using it. Swizzling Should Always Be Done in In Objective-C, the runtime automatically calls two methods on every class. is called when the class is first loaded, and is called before the first class or instance method is invoked. Both methods are optional and are only called if implemented. Because affects the global state of a class, it is important to avoid race conditions in concurrent contexts. is guaranteed to be called during class initialization and ensures that behavioral changes at the application level are applied consistently. In contrast, does not offer this guarantee — in fact, if no messages are sent to the class within the app, it may never be called at all. Swizzling Should Always Be Done in For the same reason as above — since swizzling modifies global state — we need to take precautions at runtime. Atomicity is one such precaution: it ensures the code is only executed once, regardless of how many threads are running. GCD's guarantees this behavior and should be treated as a best practice when doing method swizzling. Selectors, Methods, and Implementations In Objective-C, selectors (), methods (), and implementations () are special points in the runtime, though these terms are more commonly used when describing the message-sending process. Here are some descriptions from the : 1. : Used to represent the name of a method at runtime. A method selector is a C string registered with the Objective-C runtime. Selectors are generated by the compiler and automatically mapped by the runtime when the class is loaded. 2. : Represents the type of a method in a class definition. 3. : A pointer type that points to the start of a method's implementation function. This function follows the standard C calling convention for the current CPU architecture. The first parameter is a pointer to the object itself (), the second is the method selector, followed by the actual method arguments. The best way to understand the relationship among these three concepts is: a class maintains a runtime dispatch table for receivable messages. Each entry in that table is a Method, keyed by a specific name called a Selector (SEL), which maps to an Implementation (IMP) — a pointer to the underlying C function. To swizzle a method, we remap an existing selector in the dispatch table to a different implementation, while binding the original implementation to a new selector. Calling Looking back at the implementation of our swizzled method, it might seem like it would cause an infinite loop. Surprisingly, it does not. During swizzling, the call inside the method has already been redirected to UIViewController's . In this situation, no infinite loop occurs. However, if we called , an infinite loop would happen, because that method's implementation has already been reassigned to at runtime. Caveats Swizzling is often referred to as "black magic" and can produce unpredictable behavior and unforeseen consequences. While it is not the safest technique, it can be used safely if you follow these precautions: Always call the original implementation (unless there is a very good reason not to): An API provides a contract between input and output, but its internal implementation is a black box. Swizzling a method without calling the original implementation can corrupt private state or underlying operations, affecting other parts of your program. Avoid conflicts: Add a prefix to your custom category methods to prevent naming conflicts with any libraries you depend on. Understand what you're doing: Simply copying and pasting swizzle code without understanding how it works is not only dangerous, but also wastes an opportunity to learn about the Objective-C runtime. Read the and examine the header to understand how everything works. Be cautious: No matter how confident you are when swizzling Foundation, UIKit, or other built-in frameworks, remember that many things may be different in the next OS version. Code All code from this article can be found on my GitHub .