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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值