After getting started developing iOS for a while, I found that I cannot just focus on completing the needs and use my spare time to study other development skills to improve my level in a limited time. Of course, the proposition of "other development skills" doesn't seem to be a marginal for any development field, and for me, trying to get to objc/runtime is the first step to start exploring iOS development in depth.
I just learned about runtime. Of course, I need to start with a relatively simple API. Today I will list and sort out the relevant points of class_addMethod:
Start with the documentation first.
/** * Adds a new method to a class with a given name and implementation. * * @param cls The class to which to add a method. * @param name A selector that specifies the name of the method being added. * @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd. * @param types An array of characters that describe the types of the arguments to the method. * * @return YES if the method was added successfully, otherwise NO * (for example, the class already contains a method implementation with that name). * * @note class_addMethod will add an override of a superclass's implementation, * but will not replace an existing implementation in this class. * To change an existing implementation, use method_setImplementation. */ OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
To translate it roughly, the function of this method is to add a new method and a specific implementation of this method to the class. Analyze the parameters required for this method:
Class cls
The cls parameter indicates the class that needs to be added to a new method.
SEL name
The name parameter represents the method name of the selector and can be named by yourself according to your preferences.
IMP imp
imp is implementation, which represents a pointer to the implementation method generated by the compiler. In other words, the method that this pointer points to is the method we want to add.
const char *types
The last parameter *types represents the return value and parameters of the method we want to add.
After briefly introducing the required parameters and functions in class_addMethod, we can start using this method to add the methods we need! Before using it, we must first clarify that Objective-C is a dynamic language. It will place part of the code in the runtime process, not compile time. Therefore, when executing the code, not only is the compiler needed, but also a runtime environment (Runtime). In order to meet some needs, Apple open source Runtime Source and provides an open API for developers to use.
Secondly, we need to know under what circumstances we need to call the class_addMethod method. When you are in the project, you need to inherit a certain class (subclass), but the parent class does not provide the calling method I need, and I am not clear about the specific implementation of some methods in the parent class; or, I need to write a category for this class, in which I may need to replace/add a certain method (note: it is not recommended to override the method in the classification, and it is also impossible to obtain the so-called parent class methods through super). In roughly two cases, we can achieve the effect we want through class_addMethod .
Okay, after saying so much, how should we call it? If you are not sure about how to use it, then reading the manual is the best way. There are detailed usage methods in the documentation provided by Apple (Objective-C Runtime Programming Guide - Dynamic Method Resolution). The following content uses the myCar class to explain the specific usage rules in detail:
First of all, since we want to add our method to a certain class, we should inherit or write a classification for this class. Here I create a new class called "myCar" as a classification of the "Car" class.
#import "Car+" @implementation Car (myCar) @end
We know that in Objective-C, normal calling methods are implemented through message mechanism. If no message method is found in the class, the system will enter the processing process where the method cannot be found. If we add the new method we need in this process, we can realize dynamic addition during the operation. This process or mechanism is Objective-C's Message Forwarding
There are two main methods involved in this mechanism:
+ (BOOL)resolveInstanceMethod:(SEL)sel + (BOOL)resolveClassMethod:(SEL)sel
The only difference between the two methods is whether the static method or the instance method needs to be added. Let’s take the former as an example. Since we want to add a method, we will implement it in the "myCar" class. The code is as follows:
#import "Car+" void startEngine(id self, SEL _cmd) { NSLog(@"my car starts the engine"); } @implementation Car (myCar) @end
At this point, we implemented the startEngine method we want to add. This is a C function, which contains at least two parameters: self and _cmd (self represents the function itself, while _cmd is a SEL data body, including the specific method address). What if you want to add new parameters to this method? See the following code:
#import "Car+" void startEngine(id self, SEL _cmd, NSString *brand) { NSLog(@"my %@ car starts the engine", brand); } @implementation Car (myCar) @end
Just add the required parameters and types after those two necessary parameters. The same goes for the return value. Just modify the void before the method name to the return type we want. Here we do not need to return the value.
Next, we overload resolveInstanceMethod: this function:
#import "Car+" #import <objc/> void startEngine(id self, SEL _cmd, NSString *brand) { NSLog(@"my %@ car starts the engine", brand); } @implementation Car (myCar) + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(drive)) { class_addMethod([self class], sel, (IMP)startEngine, "v@:@"); return YES; } return [super resolveInstanceMethod:sel]; } @end
To explain, this function will be executed in the runtime environment if the implementation of this method is not found. The first line determines whether the passed SEL name matches, and then calls the class_addMethod method to pass in the corresponding parameters. The third parameter passed in is the implementation of the C language function we added, that is, the name of the third parameter must be consistent with the specific function name added. The fourth parameter refers to the return value of the function and the parameter content.
As for the return value of this method, when I tested it, no matter what the BOOL value was, it will not affect our execution target. Generally, it can just return YES.
If you feel that writing new functions in C style is not suitable, you can rewritten it into the following code:
@implementation Car (myCar) + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(drive)) { class_addMethod([self class], sel, class_getMethodImplementation(self, @selector(startEngine:)), "s@:@"); return YES; } return [super resolveInstanceMethod:sel]; } - (void)startEngine:(NSString *)brand { NSLog(@"my %@ car starts the engine", brand); } @end
where class_getMethodImplementation means to obtain a pointer to the specific implementation of SEL.
Then create a new class "DynamicSelector", in which we implement the dynamic addition method to "Car".
#import "" #import "Car+" @implementation DynamicSelector - (void)dynamicAddMethod { Car *c = [[Car alloc] init]; [c performSelector:@selector(drive) withObject:@"bmw"]; } @end
Note that we cannot use [self method:] to call here, because the method we added is executed at runtime, and the compiler is only responsible for the method retrieval at compile time. Once an object's drive method is not retrieved, an error will be reported. So here we use performSelector: withObject: to call, save, and run.
2016-08-26 10:50:17.207 objc-runtime[76618:3031897] my bmw car starts the engine Program ended with exit code: 0
The print results are consistent with what we expect to achieve. If you need to return a value, the method is similar.
The above is the iOS Runntime dynamically adding class methods and calling -class_addMethod that the editor introduced to you. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support for my website!