Use runtime to dynamically add and call methods for the class.

Source: Internet
Author: User

Use runtime to dynamically add and call methods for the class.

After I started iOS development for a while, I found that I could not just focus on completing the needs, but I could use other development skills in my spare time to improve my skills within a limited time. Of course, the proposition of "other development skills" is not marginal to any development field. For me, trying to access objc/runtime is the first step to start in-depth exploration of iOS development.

To understand runtime, of course, we should start with a relatively simple api. Today we will list the related points of class_addMethod:

Start with the document.

/**  * 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);

The role of this method is to add a new method to the class and the specific implementation of this method. Analyze the parameters required for this method:

Class cls

The cls parameter indicates the class for which a new method is to be added.

SEL name

The name parameter indicates the method name of the selector. you can name it as you like.

IMP imp

Imp is implementation, which indicates the pointer generated by the compiler to the implementation method. That is to say, the Pointer Points to the method we want to add.

const char *types

The last parameter * types indicates the return value and parameters of the method to be added.

 

After briefly introducing the parameters and functions required in class_addMethod, we can start to use this method to add the methods we need! Before using Objective-C, we must first define Objective-C as a dynamic language, which places part of the code for execution during runtime rather than during compilation, therefore, when executing code, you not only need a compiler, but also a Runtime environment (Runtime). To meet some requirements, apple open-Source Runtime Source and provides open APIs for developers to use.

Next, we need to know under what circumstances we need to call the class_addMethod method. When a project needs to inherit a class (subclass), but the parent class does not provide the call method that I need, and I do not know the specific implementation of some methods in the parent class; alternatively, I need to write a category for this class. In this category, I may need to replace/Add a new method (Note: it is not recommended to override the method in the category, in addition, super cannot be used to obtain the method of the so-called parent class ). In both cases, we can use class_addMethod to achieve the desired effect.

Well, how should I call it if I say so much? If you do not know how to use it, reading the manual is the best method. Detailed usage (Objective-C Runtime Programming Guide-Dynamic Method Resolution) is provided in the documentation provided by Apple ), the following describes the specific rules using the myCar class:

First, since we want to add our methods to a class, we should inherit or write a category for this class. Here we create a class named "myCar, as the category of the Car class.

#import "Car+myCar.h"@implementation Car (myCar)@end

We know that in Objective-C, the normal call method is implemented through the message mechanism (message). If no message method is found in the class, the system will enter the process in which the method cannot be found. If we add the new method we need in this process, we can dynamically add it during the running process. This process or mechanism is the Message Forwarding of Objective-C.

The mechanism involves two main methods:

+ (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 for example. Since we want to add a method, we will implement it in the "myCar" class. The Code is as follows:

#import "Car+myCar.h"void startEngine(id self, SEL _cmd) {    NSLog(@"my car starts the engine");}@implementation Car (myCar)@end

So far, we have implemented the startEngine method we want to add. This is a C-language function. It contains at least two parameters: self and _ cmd (self represents the function itself, while _ cmd is a SEL data body, contains the specific method address ). What if we want to add parameters in this method? See the following code:

#import "Car+myCar.h"void startEngine(id self, SEL _cmd, NSString *brand) {    NSLog(@"my %@ car starts the engine", brand);}@implementation Car (myCar)@end

You only need to add the required parameters and types after the two required parameters. Similarly, you only need to change the void before the method name to the desired return type, here we do not need to return values.

Next, we reload resolveInstanceMethod: This function:

#import "Car+myCar.h"#import <objc/runtime.h>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

This function is executed in the runtime environment if the implementation of this method is not found. The first line determines whether the imported SEL name matches, then calls the class_addMethod method, and passes in the corresponding parameters. The third parameter is the implementation of the added C-language function. That is to say, the name of the third parameter must be consistent with the name of the added function. The fourth parameter refers to the return value of the function and the content of the parameter.

As for the return value of this type of method, No matter what BOOL value is during my testing, it does not affect our execution goal. Generally, YES is returned.

If you think it is not appropriate to write new functions in the C language style, you can rewrite the Code as follows:

@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

Class_getMethodImplementation means to get the specific implementation pointer of SEL.

Then create a new class "DynamicSelector". In this new class, we dynamically add the "Car" method.

#import "DynamicSelector.h"#import "Car+myCar.h"@implementation DynamicSelector- (void)dynamicAddMethod {    Car *c = [[Car alloc] init];    [c performSelector:@selector(drive) withObject:@"bmw"];}@end

Note that [self method:] cannot be used for calling here, because the method we add is executed at runtime, And the compiler is only responsible for retrieval of methods during compilation, once the drive method of an object is not retrieved, an error is reported. So here we use javasmselector: withObject: to call, save, and run it.

2016-08-26 10:50:17.207 objc-runtime[76618:3031897] my bmw car starts the engineProgram ended with exit code: 0

The printed results meet our expected goals. If a return value is required, the method is similar.

 

The project has been uploaded to the https://github.com/zhangqifan/class_addMethod and you can clone the source code as needed. If you need to correct it, please push issue.

 

I am original. For more information, see the link.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.