In associated objects last week, we began to explore some of the dark spells of the OBJECTIVE-C runtime. This week we went on to discuss perhaps the most controversial run-time technology: Method swizzling.
Method swizzling refers to the process of changing the implementation of an existing selector, which relies on the invocation of a method in Objectvie-c to change at runtime-by altering the mapping between selectors in the class's dispatch table (Dispatch table) and the final function. For example, let's say we want to track the number of times each view controller is presented to a user in an iOS app: we can add the corresponding tracking code to each view controller's Viewwillappear: implementation method, but doing so produces a lot of duplicated code. Subclasses may be another option, but requires you to subclass Uiviewcontroller, Uitableviewcontroller, Uinavigationcontroller, and all other view controller classes, which can also cause code duplication. Fortunately, there is another way to do the method swizzling in the classification, and here's how to do it:
- #import
- @implementation Uiviewcontroller (Tracking)
- + (void) load {
- static dispatch_once_t Oncetoken;
- Dispatch_once (&oncetoken, ^{
- Class class = [self class];
- //When swizzling A-class method, use the following:
- //Class class = Object_getclass ((id) self);
- SEL originalselector = @selector (viewwillappear:);
- SEL swizzledselector = @selector (xxx_viewwillappear:);
- Method Originalmethod = Class_getinstancemethod (class, originalselector);
- Method Swizzledmethod = Class_getinstancemethod (class, swizzledselector);
- BOOL Didaddmethod =
- Class_addmethod (class,
- Originalselector,
- Method_getimplementation (Swizzledmethod),
- Method_gettypeencoding (Swizzledmethod));
- if (didaddmethod) {
- Class_replacemethod (class,
- Swizzledselector,
- Method_getimplementation (Originalmethod),
- Method_gettypeencoding (Originalmethod));
- } Else {
- Method_exchangeimplementations (Originalmethod, Swizzledmethod);
- }
- });
- }
- #pragma mark-method swizzling
- -(void) Xxx_viewwillappear: (BOOL) Animated {
- [Self xxx_viewwillappear:animated];
- NSLog (@"viewwillappear:%@", self);
- }
- @end
In computer science, pointer Transformation (pointer swizzling) refers to converting a reference based on a name or position to a direct pointer reference. However, in Objective-c, the origin of the word is not fully known, but the reference to this is actually very well understood, method swizzling can be changed by the selector of its reference function pointer. Now, when Uiviewcontroller or any instance of its subclass triggers Viewwillappear: The method prints a log journal. Injecting operations into the life cycle of a view controller, responding to events, drawing views, or a network stack in the foundation is a scenario where you can use method swizzling to produce significant results. There are other scenarios where using swizzling will be the right choice, which will become increasingly apparent as the objective-c developer experience continues to enrich. Let's not say why and where to use swizzling, to see how it should be implemented:
+load vs. +initializeSwizzling should be implemented in the +load method. These two methods of each class are called automatically by the Objective-c runtime system, +load is called when a class is initially loaded, and +initialize is called before the first time the class or its instance is called in the application. Both of these methods are optional and will be executed only if they are implemented. Because method swizzling can affect the overall situation, it is important to reduce risk. +load can guarantee that it will be loaded when the class is initialized, which provides some uniformity in changing the behavior of the system. But +initialize does not guarantee when it will be invoked-in fact it may never be called, for example, when an application never sends a message directly to the class.
dispatch_onceSwizzling should be implemented in dispatch_once. Or because swizzling will change the overall situation, we need to take all available precautions at runtime. Safeguarding atomicity is a measure that ensures that code is executed only once in a multithreaded environment. The diapatch_once in GCD provides these guarantees and should be used as a standard practice for swizzling.
selectors, methods and implementationsIn Objective-c, although these words are often put together to describe the process of message passing, selectors, methods, and implementations represent different aspects of the runtime, respectively. Here is a description of them in the Apple Objective-c Runtime reference documentation: 1. Selector (typedef struct OBJC_SELECTOR *sel): The selector is used to represent the name of a method at run time, The selector of a method is a C string registered to (or mapped to) the OBJECTIVE-C runtime, which is generated by the compiler and automatically mapped by the runtime when the class is loaded. 2. Method (typedef struct OBJC_METHOD *method): An unknown type that represents a method in a class definition. 3. Implementation (typedef ID (*IMP) (ID, SEL, ...) ): This data type is a pointer to the start of a function that implements a method, using the standard C call protocol based on the current CPU architecture. The first argument is a pointer to self (that is, the memory space for an instance of the class, or a pointer to a meta-class (Metaclass) for a class method). The second parameter is the selector of the method, followed by the parameters. The best way to understand the relationship between these concepts is that a class maintains a dispatch table (dispatch table) for parsing messages sent at runtime, and each entity in the dispatch table (entry) is a method, where the key value is a unique name- Selector (SEL), which corresponds to an implementation (IMP)--is actually a pointer to a standard C function. Method Swizzling is to change the schedule of the class to allow the message parsing from one selector to another implementation, while confusing the original method implementation to a new selector.
Call _cmdThe following code looks like it will cause a dead loop:
- -(void) Xxx_viewwillappear: (BOOL) Animated {
- [Self xxx_viewwillappear:animated];
- NSLog (@"viewwillappear:%@", Nsstringfromclass ([Self class]);
- }
But in fact, in the process of swizzling, Xxx_viewwillappear: will be reassigned to Uiviewcontroller's-viewwillappear: the original implementation. The intuition that a good programmer should have will tell you that invoking the current method by self in the implementation of a method produces an error, but in this case, it makes more sense if we remember what's going on. Instead, if we call Viewwillappear in this method: it will really lead to a dead loop, because the implementation of this method will be swizzle to viewwillappear at runtime: the selector. Remember to prefix the swizzled method with the prefix you need to add to the classification method that might conflict.
PrecautionsSwizzling is widely regarded as a kind of witchcraft, which leads to unpredictable behavior and results. Although not the safest, the method swizzling is safe if you take these steps.
1. Always call the original implementation of the method (unless you have enough reason not to do so):The API provides a protocol for input and output, but it is actually a black box, and the original implementation may break some private state, even other parts of the program, in the method swizzling process.
2. Avoid conflicts:To prefix the classification method, make sure that you do not allow other code in your code base (or dependent libraries) to do the same thing as you.
3. Understanding:Simply copying and pasting swizzling code without understanding how it works is not only very dangerous, but also a waste of the opportunity to learn objective-c run time. Read the Objective-c Runtime Reference and understand how and why the code is executed, and try to use your understanding to eliminate your doubts. Be cautious: No matter how confident you are, you can swizzling Foundation, UIKit, or other built-in frameworks, keep in mind that all of this may not be good in the next release. Be prepared ahead of time to avoid the time to burn. Dare not trust the bold direct use of the OBJECTIVE-C runtime? Jonathan ' Wolf ' Rentzsch offers a proven, cocoapads-backed library jrswizzle that will give you everything you can think of. Like associated objects, method swizzling is a powerful technique, but you should also use caution.
OBJECTIVE-C runtime four: Method swizzling