iOS用runtime给一个类动态添加方法 ---class_addMethod

先介绍下class_addMethod这个fangfa

 

  1.  
    /**
  2.  
    * Adds a new method to a class with a given name and implementation.
  3.  
    *
  4.  
    * @param cls The class to which to add a method.
  5.  
    * @param name A selector that specifies the name of the method being added.
  6.  
    * @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
  7.  
    * @param types An array of characters that describe the types of the arguments to the method.
  8.  
    *
  9.  
    * @return YES if the method was added successfully, otherwise NO
  10.  
    * (for example, the class already contains a method implementation with that name).
  11.  
    *
  12.  
    * @note class_addMethod will add an override of a superclass's implementation,
  13.  
    * but will not replace an existing implementation in this class.
  14.  
    * To change an existing implementation, use method_setImplementation.
  15.  
    */
  16.  
    OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
  17.  
    const char *types)
  18.  
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);


这是文档 

 

描述就是:给一个类添加一个新的方法和该方法的具体实现

参数:

第一个

 

Class cls

cls :你要添加新方法的那个类 

 

第二个

 

SEL name

name :要添加的方法 

 

第三个

 

IMP imp

imp:指向实现方法的指针   就是要添加的方法的实现部分 

 

第四个

 

const char *types

*types:我们要添加的方法的返回值和参数   叫 type encodings 这个东西怎么写我就不说了 可以去查  在这里我有一个懒办法得到它不用暴力手打   往后看 

 

 

然后这个方法的描述和参数说完了,下面说说他用在哪,

给一个类用运行时添加一个方法   他可以用来解决我们开发中的这个问题------“unrecognized selector sent to instance 0x7faa2a132c0”  

在OC中我们调用一个方法时 有个关键字 叫  “消息机制”    就是执行  [receiver message]; 这句代码其实就是发送了一个消息 (发消息的详细过程我就不写了 感兴趣 去查),

消息里面带着你调用这个方法的名称和参数以及是哪个对象发送的消息,然后 发出消息后  如果有对应的方法响应,就执行方法,没有的话就报“unrecognized selector sent to instance 0x7faa2a132c0” ,然后就crash   ,为了防止crash我们可以用class_addMethod给找不到对应方法即将crash的消息添加一个与之对应的方法来防止其因为找不到相应方法而crash

在OC中找不到对相应的实现方法时  有补救机制 即 会先调用动态决议方法   该方法解决不了问题 再调用重定向方法    都解决不了问题是 crash

动态决议方法:

 

  1.  
    + (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
  2.  
    + (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);


重定向方法: 

 

 

- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

 

 

下面上代码:给一个类的对象调用一个未实现的方法  然后用runtime 在动态决议方法中为其添加实现  防止crash 

Person类

 

  1.  
    #import "Person.h"
  2.  
    #import <objc/runtime.h>
  3.  
     
  4.  
    @implementation Person
  5.  
     
  6.  
     
  7.  
    + (BOOL)resolveInstanceMethod:(SEL)sel {
  8.  
    Method exchangeM = class_getInstanceMethod([self class], @selector(eatWithPersonName:));
  9.  
    class_addMethod([self class], sel, class_getMethodImplementation(self, @selector(eatWithPersonName:)),method_getTypeEncoding(exchangeM));
  10.  
    return YES;
  11.  
    }
  12.  
     
  13.  
    - (void)eatWithPersonName:(NSString *)name {
  14.  
    NSLog(@"Person %@ start eat ",name);
  15.  
    }
  16.  
     
  17.  
     
  18.  
     
  19.  
    @end


里面重写resolveInstanceMethod方法  给找不到实现的方法 添加实现  - (void)eatWithPersonName:(NSString *)name; 

 

第一句

    Method exchangeM = class_getInstanceMethod([self class], @selector(eatWithPersonName:));

是为下面method_getTypeEncoding(exchangeM) 做铺垫  这样就拿到了eatWithPersonName方法的type encodings 用作class_addMethod的第四个参数,就是上面我提到的不用暴力手打的获取type encodings的方法

然后调用 class_addMethod  为当前类[self class] 的sel 方法 添加实现 class_getMethodImplementation(self, @selector(eatWithPersonName:)) 这一串就是拿到方法eatWithPersonName的IMP指针  然后最后一个参数是  type encodings

 

然后测试

  1.  
    Person *person = [[Person alloc]init];
  2.  
    //调用Person未实现方法eat
  3.  
    [person performSelector:@selector(eat) withObject:@"minzhe"];

运行结果: 

 

 

2017-07-14 14:41:35.297 RunTimeAddMethod[1458:379458] Person minzhe start eat 


好了   添加上了    这样就没有 crash

posted @ 2020-12-14 16:58  ios_福  阅读(584)  评论(0编辑  收藏  举报