zoukankan      html  css  js  c++  java
  • 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:

     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 OtherPerson
    10 
    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->_cmd
    59         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 push
    11 #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 Instance
    21         //发送消息给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

  • 相关阅读:
    将一个dropdownlist从一个div复制到另一个div
    【转】AOP 那点事儿(续集)
    【转】AOP 那点事儿
    spring 简单配置
    spring 基础原理
    jvm简介
    jre、jdk和jvm的关系
    【转】Jvm工作原理
    servlet简述
    Filter简述
  • 原文地址:https://www.cnblogs.com/goodboy-heyang/p/5126557.html
Copyright © 2011-2022 走看看