directory

1. Basic Concepts

Second, the concrete implementation of runtime

Three, common methods

1, obtain attribute list 2, obtain method list 3, obtain member variable list 4, obtain protocol list 5, obtain class method and instance method and method exchange 6, add method

Four, other

5. Reference materials


1. Basic Concepts

Objective-c interacts with the Runtime system at three different levels:

  • 1. Through objective-C source code;
  • 2. Methods defined by the Foundation framework’s NSObject class;
  • 3. By calling the Runtime function directly.

Most of the time you just write your Objc code, and the Runtime system automatically works behind the scenes.

Second, the concrete implementation of runtime

When we write a piece of code in OC [tableView cellForRowAtIndexPath: indexPath]; At compile time the RunTime will convert this code to objc_msgSend(tableView, @selector(cellForRowAtIndexPath:),indexPath);

Three, common methods

1. Get the property list

//People.h
@interface People : NSObject

@property (nonatomic.strong) NSString *name;
@property (nonatomic.assign) NSUInteger age;

@end

//People.m
@interface People(a){
    NSString *aaa;
}

@property (nonatomic.strong) NSString *fatherName;

@end

@implementation People {
    NSString *bbb;
}

@end

Copy the code
// Get all attributes- (void)getAllProperty {
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList([People class], &count);
    for (unsigned int i=0; i<count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"property---->%@"[NSStringstringWithUTF8String:propertyName]); }}Copy the code

Print result:

Conclusion:

  • 1) Attributes defined in. H files or in. M files can be obtained by obtaining the attribute list method;
  • 2) Member variables are different from attributes and cannot be obtained by this method;
  • 3) The attributes in the.m file are output first, and then the attributes in the.h file are saved according to the order in which they are defined.

2. Get the list of methods

//People.h
@interface People : NSObject

@property (nonatomic.strong) NSString*name; - (void)iPrintName; + (void)cPrintName;

@end

//People.m
@interface People(a){
    NSString *aaa;
}

@property (nonatomic.strong) NSString *fatherName;

@end

@implementation People {
    NSString *bbb;
}

/ / no implementation
//-(void)iPrintName {
//    
/ /}+ (void)cPrintName {
    
}

-(void)printAge {
    
}

@end

Copy the code
// Get a list of methods (excluding class methods)- (void)getAllMethod {
    unsigned int count = 0;
    Method *methodList = class_copyMethodList([People class], &count);
    for (unsigned int i=0; i<count; i++) {
        Method method = methodList[i];
        NSLog(@"method---->%@".NSStringFromSelector(method_getName(method))); }}Copy the code

Conclusion:

  • 1) Class methods cannot be retrieved from this function;
  • 2) Only methods implemented in.m files can be retrieved. Methods defined in.h files cannot be retrieved if they are not implemented in.m.
  • Setters and getters are automatically generated for @property attributes, and can also be retrieved by this method.
  • 4) the.m implementation also hides a.cxx_destruct method, which is a common delloc method in oc;
  • 5) The saving order is the first to save the getter and setter methods automatically generated by the user in the.m file, followed by the hidden delloc methods, and finally the getter and setter methods automatically generated by the.h property.

3. Get the list of member variables

//People.h
@interface People : NSObject {
    NSString *cccc;
}

@property (nonatomic.strong) NSString *name;

@end

//People.m
@interface People(a){
    NSString *aaa;
}

@property (nonatomic.strong) NSString *fatherName;

@end

@implementation People {
    NSString *bbb;
}

@end
Copy the code
-(void)getAllIvar{ unsigned int count = 0; Ivar *ivarList = class_copyIvarList([People class], &count); for (unsigned int i=0; i<count; i++) { Ivar myIvar = ivarList[i]; const char *ivarName = ivar_getName(myIvar); NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]); }}Copy the code

Print result:

Conclusion:

  • 1) Member variables are saved from the.h file and then from the.m file;
  • 2) @property will automatically generate member variables starting with _, also saved in the.h file generated, then saved in the.m file generated.

4. Obtain the protocol list

//People.h
@protocol PeopleDelegate <NSObject>- (void)people;

@end

@interface People : NSObject

@property (nonatomic.strong) NSString *name;
//@property (nonatomic, weak) id <PeopleDelegate> delegate;
@end

//People.m
@interface People(a)

@property (nonatomic.strong) NSString *fatherName;

@end

@implementation People

@end

//ViewController.m
@interface ViewController()"PeopleDelegate.UITabBarDelegate.UITableViewDataSource>

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self getProtocal];
}
@end
Copy the code
// Get the protocol list- (void)getProtocal {
    unsigned int count = 0;
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);  // This becomes self
    for (unsigned int i = 0; i<count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol---->%@"[NSStringstringWithUTF8String:protocolName]); }}Copy the code

Print result:

Conclusion:

  • 1) As long as you declare the protocol, when referencing, you can get the list of the protocols contained in the class, even if you do not specify the object of the proxy, and there is no method to implement the protocol

5. Get class method and instance method and method exchange

//People.h
@interface People : NSObject

@property (nonatomic.strong) NSString*name; - (void)printInstanceMethod; + (void)printClassMethod;

@end

//People.m
@interface People(a)

@property (nonatomic.strong) NSString *fatherName;

@end

@implementation People- (void)printInstanceMethod{
    NSLog(@" I am instance method"); } + (void)printClassMethod {
    NSLog(@" I'm a class method");
}
@end

Copy the code

The sample

- (void)getMethod {
    
    People * p1 = [[People alloc] init];
    
    Method m1 = class_getInstanceMethod([p1 class].@selector(printInstanceMethod));
    Method m2 = class_getClassMethod([People class].@selector(printClassMethod));
    NSLog(@" Before test:");
    [p1 printInstanceMethod];
    [People printClassMethod];
    
    method_exchangeImplementations(m1, m2);
    NSLog(@" After test:");
    [p1 printInstanceMethod];
    [People printClassMethod];
}
Copy the code

Print the result

6. Add method

Refer to the article

//Car+MyCar.h
#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];
}
Copy the code

If you are not used to C code, replace it with the following code:

 - (void)startEngine:(NSString *)brand {
    NSLog(@"my %@ car starts the engine", brand);
 }

 + (BOOL)resolveInstanceMethod:(SEL)sel {
     if (sel == @selector(drive)) {
       class_addMethod([self class], sel, class_getMethodImplementation(self.@selector(startEngine:)), "v@:@");
       return YES;
     }
     return [super resolveInstanceMethod:sel];
 }

@end
Copy the code

Call:

- (void)addMethod {
    Car *c = [[Car alloc] init];
    [c performSelector:@selector(drive) withObject:@"BMW"];
}

Copy the code

Explanation: In Objective-C, normal calling methods are done through message, so if the class doesn’t find a message method to send, the system goes into a process where it can’t find that method, and if we add the new method that we need in that process, You can implement dynamic addition during operation. This process, or mechanism, is objective-C’s Message Forwarding. There are two main methods involved in this mechanism:

+ (BOOL)resolveInstanceMethod:(SEL)sel;// Instance method
+ (BOOL)resolveClassMethod:(SEL)sel;/ / class methods
Copy the code

This function is executed in the Runtime environment if no implementation of the method is found. The first line checks if the SEL name passed in matches, and then calls the class_addMethod method, passing in the corresponding argument. The third parameter is passed the implementation of the C function we added, that is, the name of the third parameter is the same as the name of the function we added. The fourth argument refers to the return value of the function and the contents of the argument.

Results:

Four, other

At first I thought class_addMethod and class_replaceMethod were the same thing as method_exchangeImplementations, but I saw the code below and I realized that there is a different kind of implementations, Class_addMethod and class_replaceMethod suits are used when the method is not implemented, but when the method already exists, we need to use method_exchangeImplementations, And you can see that from this if (didAddMethod).

+ (void)load{
    NSString *className = NSStringFromClass(self.class);
    NSLog(@"classname %@", className);
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Pay special attention to the nature of the method you replace
        // When swizzling a Instance method, use the following:
        // Class class = [self class];

        // When swizzling a class method, use the following:
        Class class = object_getClass((id)self);

        SEL originalSelector = @selector(systemMethod_PrintLog);
        SEL swizzledSelector = @selector(ll_imageName);

        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); }}); }Copy the code

Demo


5. Reference materials

Exotic curiosity-a solution looking for 1, 2, runtime runtime explanation series 3, the most practical runtime OC, interview, work you see me is enough!


contact

Email: [email protected] Related Account:

  • Denver – Adrenine
  • Jane – Adrenine book
  • Blog – Adrenine
  • Github – Adrenine