Runtime 方法替换 和 动态添加实例方法 结合使用

简介:

前言:

方法替换,可以替换任意外部类的方法,而动态添加方法只能实现在被添加类创建的对象里,但是将方法替换和动态添加方法结合使用,可以实现,对任意外部类动态添加需要的方法,这个方法可以是类方法也可以是实例方法,这个外部类也可以是没有任何方法声明和实现的类。

主要思路:

使用运行时的方法替换将在外部类将自定义方法hy_resolveInstanceMethodhy_resolveClassMethod(用hy_前缀表示是我自定义的方法)和需要被添加的类中的resolveInstanceMethod或者resolveClassMethod方法替换,替换之前在hy_resolveInstanceMethodhy_resolveClassMethod方法内部写好本应该在resolveInstanceMethod或者resolveClassMethod方法内部写好的runtime动态添加方法的逻辑。

可能有点绕,不过至少需要继续阅读源码,思考其中的逻辑,其实不难,前提是熟悉使用runtime的方法。

 

缺陷:1、含参数的方法难以处理,参数值需要根据实际业务逻辑而定。

Before use import <objc/message.h> ,need following:

Create Person.h and Person.m

Person.h:

1 #import <Foundation/Foundation.h>
2 
3 @interface Person : NSObject
4 
5 @end

Person.m:
1 #import "Person.h"
2 
3 @implementation Person
4 
5 @end

Create OtherPerson.h and OtherPerson.m

OtherPerson.h:

1 #import <Foundation/Foundation.h>
2 
3 @interface OtherPerson : NSObject
4 
5 
6 @end

OtherPerson.m:
//
//  Created by HEYANG on 16/1/11.
//  Copyright © 2016年 HEYANG. All rights reserved.
//

#import "OtherPerson.h"
#import <objc/message.h>

@implementation OtherPerson


+(void)load{
    Class clazz = NSClassFromString(@"Person");
    
    //获取替换前的类方法
    Method instance_eat = 
        class_getClassMethod(clazz, @selector(resolveInstanceMethod:));
    //获取替换后的类方法
    Method instance_notEat = 
        class_getClassMethod(self, @selector(hy_resolveInstanceMethod:));
    
    //然后交换类方法
    method_exchangeImplementations(instance_eat, instance_notEat);
    
    //获取替换前的类方法
    Method class_eat = 
        class_getClassMethod(clazz, @selector(resolveClassMethod:));
    //获取替换后的类方法
    Method class_notEat = 
        class_getClassMethod(self, @selector(hy2_resolveClassMethod:));
    
    //然后交换类方法
    method_exchangeImplementations(class_eat, class_notEat);
    
}

void eat_1(id self,SEL sel)
{
    NSLog(@"到底吃不吃饭了");
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}
void eat_2(id self,SEL sel, NSString* str1,NSString* str2)
{
    NSLog(@"到底吃不吃饭了");
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
    NSLog(@"打印两个参数值:%@ and %@",str1,str2);
}


+(BOOL)hy_resolveInstanceMethod:(SEL)sel{
    //当sel为实现方法中 有 eat 方法
    if (sel == NSSelectorFromString(@"eat")) {
        //就 动态添加eat方法
        
        // 第一个参数:给哪个类添加方法
        // 第二个参数:添加方法的方法编号
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
        class_addMethod(self, sel, (IMP)eat_1, "v@:");
    }
    return YES;
}
+(BOOL)hy2_resolveClassMethod:(SEL)sel{
    
    if (sel == NSSelectorFromString(@"eat:with:")) {
        
        class_addMethod(objc_getMetaClass("Person"), sel, (IMP)eat_2, "v#:@@");
    }
    
    return YES;
}

@end

last In file ‘main.m’:

main.m:

/**
 *
 *  Swap Method and Dynamic add Method (交换方法和动态添加方法)
 *
 */
 
#import <Foundation/Foundation.h>

//ignore undeclared warm 忽视未声明的警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //get this Person class 拿到了这个Person类
        Class clazz = NSClassFromString(@"Person");
        //get this Person Instance 拿到这个Person实例
        id person = [[clazz alloc] init];
        
        //send message to 'eat' method in Person Class or Person Instance
        //发送消息给Person类或者Person实例的‘eat’方法 不含参数
        [person performSelector:@selector(eat) withObject:nil];
        //发送消息给Person类的‘eat’方法 含两个参数
        [clazz performSelector:@selector(eat:with:) 
                        withObject:@"Hello" 
                        withObject:@"World"];
    }
    return 0;
}

#pragma clang diagnostic pop

the code test result

the code test result picture

extra

download

download github code source

相关文章
|
Python
Python面向对象、类的抽象、类的定义、类名遵循大驼峰的命名规范创建对象、类外部添加和获取对象属性、类内部操作属性魔法方法__init__()__str__()__del__()__repr__()
面向对象和面向过程,是两种编程思想. 编程思想是指对待同一个问题,解决问题的套路方式.面向过程: 注重的过程,实现的细节.亲力亲为.面向对象: 关注的是结果, 偷懒.类和对象,是面向对象中非常重要的两个概念object 是所有的类基类,即最初始的类class 类名(object): 类中的代码PEP8代码规范:类定义的前后,需要两个空行 创建的对象地址值都不一样如dog和dog1的地址就不一样,dog的地址为2378043254528dog1的地址为2378044849840 8.类内部操作属性 sel
256 1
Python面向对象、类的抽象、类的定义、类名遵循大驼峰的命名规范创建对象、类外部添加和获取对象属性、类内部操作属性魔法方法__init__()__str__()__del__()__repr__()
lodash遍历并继承对象属性,自定义分配
lodash遍历并继承对象属性,自定义分配
140 0
lodash创建一个新的对象,对象的属性名是和传入对象一样,值则在函数中修改
lodash创建一个新的对象,对象的属性名是和传入对象一样,值则在函数中修改
119 0
lodash创建对象的直接和继承属性名为数组
lodash创建对象的直接和继承属性名为数组
76 0
【错误记录】Groovy 扩展方法调用报错 ( 静态扩展方法 或 实例扩展方法 需要分别配置 | 没有配置调用会报错 groovy.lang.MissingMethodException )
【错误记录】Groovy 扩展方法调用报错 ( 静态扩展方法 或 实例扩展方法 需要分别配置 | 没有配置调用会报错 groovy.lang.MissingMethodException )
576 0
【错误记录】Groovy 扩展方法调用报错 ( 静态扩展方法 或 实例扩展方法 需要分别配置 | 没有配置调用会报错 groovy.lang.MissingMethodException )
【Groovy】Groovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )
【Groovy】Groovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )
201 0
【Groovy】Groovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )
【Groovy】Groovy 方法调用 ( Groovy 构造函数中为成员赋值 | Groovy 函数的参数传递与键值对参数 | 完整代码示例 )
【Groovy】Groovy 方法调用 ( Groovy 构造函数中为成员赋值 | Groovy 函数的参数传递与键值对参数 | 完整代码示例 )
385 0
【Groovy】Groovy 方法调用 ( Groovy 构造函数中为成员赋值 | Groovy 函数的参数传递与键值对参数 | 完整代码示例 )
【错误记录】Groovy 闭包使用报错 ( 闭包中不能直接使用外部对象的方法 | 需要先设置 delegate 代理 )
【错误记录】Groovy 闭包使用报错 ( 闭包中不能直接使用外部对象的方法 | 需要先设置 delegate 代理 )
221 0
【错误记录】Groovy 闭包使用报错 ( 闭包中不能直接使用外部对象的方法 | 需要先设置 delegate 代理 )
|
前端开发 Java
React Native之最构建对象通过构造方法传递值然后再获取值
React Native之最构建对象通过构造方法传递值然后再获取值
155 0
React Native之最构建对象通过构造方法传递值然后再获取值
|
Java
【Groovy】循环控制 ( Java 语法循环 | 默认的 IntRange 构造函数 | 可设置翻转属性的 IntRange 构造函数 | 可设置是否包含 to 的构造函数 | 0..9 简写 )(一)
【Groovy】循环控制 ( Java 语法循环 | 默认的 IntRange 构造函数 | 可设置翻转属性的 IntRange 构造函数 | 可设置是否包含 to 的构造函数 | 0..9 简写 )(一)
159 0