当前位置: 代码迷 >> Iphone >> Runtime 步骤替换 和 动态添加实例方法 结合使用
  详细解决方案

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

热度:276   发布时间:2016-04-25 05:23:50.0
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 : NSObject4 5 @end
Person.m:
1 #import "Person.h"2 3 @implementation Person4 5 @end

Create OtherPerson.h and OtherPerson.m

OtherPerson.h:

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

OtherPerson.m:

 1 // 2 //  Created by HEYANG on 16/1/11. 3 //  Copyright © 2016年 HEYANG. All rights reserved. 4 // 5  6 #import "OtherPerson.h" 7 #import <objc/message.h> 8  9 @implementation OtherPerson10 11 12 +(void)load{13     Class clazz = NSClassFromString(@"Person");14     15     //获取替换前的类方法16     Method instance_eat = 17         class_getClassMethod(clazz, @selector(resolveInstanceMethod:));18     //获取替换后的类方法19     Method instance_notEat = 20         class_getClassMethod(self, @selector(hy_resolveInstanceMethod:));21     22     //然后交换类方法23     method_exchangeImplementations(instance_eat, instance_notEat);24     25     //获取替换前的类方法26     Method class_eat = 27         class_getClassMethod(clazz, @selector(resolveClassMethod:));28     //获取替换后的类方法29     Method class_notEat = 30         class_getClassMethod(self, @selector(hy2_resolveClassMethod:));31     32     //然后交换类方法33     method_exchangeImplementations(class_eat, class_notEat);34     35 }36 37 void eat_1(id self,SEL sel)38 {39     NSLog(@"到底吃不吃饭了");40     NSLog(@"%@ %@",self,NSStringFromSelector(sel));41 }42 void eat_2(id self,SEL sel, NSString* str1,NSString* str2)43 {44     NSLog(@"到底吃不吃饭了");45     NSLog(@"%@ %@",self,NSStringFromSelector(sel));46     NSLog(@"打印两个参数值:%@ and %@",str1,str2);47 }48 49 50 +(BOOL)hy_resolveInstanceMethod:(SEL)sel{51     //当sel为实现方法中 有 eat 方法52     if (sel == NSSelectorFromString(@"eat")) {53         //就 动态添加eat方法54         55         // 第一个参数:给哪个类添加方法56         // 第二个参数:添加方法的方法编号57         // 第三个参数:添加方法的函数实现(函数地址)58         // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd59         class_addMethod(self, sel, (IMP)eat_1, "v@:");60     }61     return YES;62 }63 +(BOOL)hy2_resolveClassMethod:(SEL)sel{64     65     if (sel == NSSelectorFromString(@"eat:with:")) {66         67         class_addMethod(objc_getMetaClass("Person"), sel, (IMP)eat_2, "v#:@@");68     }69     70     return YES;71 }72 73 @end

last In file ‘main.m’:

main.m:

 1 /** 2  * 3  *  Swap Method and Dynamic add Method (交换方法和动态添加方法) 4  * 5  */ 6   7 #import <Foundation/Foundation.h> 8  9 //ignore undeclared warm 忽视未声明的警告10 #pragma clang diagnostic push11 #pragma clang diagnostic ignored "-Wundeclared-selector"12 13 int main(int argc, const char * argv[]) {14     @autoreleasepool {15         //get this Person class 拿到了这个Person类16         Class clazz = NSClassFromString(@"Person");17         //get this Person Instance 拿到这个Person实例18         id person = [[clazz alloc] init];19         20         //send message to 'eat' method in Person Class or Person Instance21         //发送消息给Person类或者Person实例的‘eat’方法 不含参数22         [person performSelector:@selector(eat) withObject:nil];23         //发送消息给Person类的‘eat’方法 含两个参数24         [clazz performSelector:@selector(eat:with:) 25                         withObject:@"Hello" 26                         withObject:@"World"];27     }28     return 0;29 }30 31 #pragma clang diagnostic pop

the code test result

the code test result picture

extra

download

download github code source

  相关解决方案