美文网首页
runtime之动态添加方法

runtime之动态添加方法

作者: Just丶Go | 来源:发表于2018-03-06 13:58 被阅读0次
      实际上是解释消息传递的机制中的一个动态解析过程(新增)
    

    需求:runtime 动态添加方法处理调用一个未实现的方法 和 去除报错。

    案例代码

    //
    //  UsrModel.h
    //  Runtime_testing
    //
    //  Created by ZTL_Sui on 2018/2/28.
    //  Copyright © 2018年 ZTL_Sui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    @interface UsrModel : NSObject
    - (NSString *)run:(NSNumber *)meter;
    - (NSNumber *)eat;
    @end
    

    IMP

    //
    //  UsrModel.m
    //  Runtime_testing
    //
    //  Created by ZTL_Sui on 2018/2/28.
    //  Copyright © 2018年 ZTL_Sui. All rights reserved.
    //
    
    #import "UsrModel.h"
    #import <objc/runtime.h>
    
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wincomplete-implementation"
    @implementation UsrModel
    #pragma clang diagnostie pop
    
    /// 此处方法可以改写为OC实例方法
    NSString * _c_method (id self, SEL _cmd, NSNumber *meter)
    {
        NSLog(@"跑了 %@米", meter);
        return @"333";
    }
    /**
     * OC底层是通过消息机制来进行消息发送的,而不是静态语言的函数调用(改)。
     * 向一个对象发送消息的处理流程
     -->对象接收到消息
     -->查看缓存中是否有匹配的方法,如果有则响应,否则继续
     -->查看对象的类对象的方法列表(method list)中是否有匹配方法,如果有则响应,否则继续
     -->查看对象的父类的缓存和方法列表中是否有匹配方法,如果有则响应,没有继续
     -->进入动态解析 +(BOOL)resolveInstanceMethod:(SEL)sel;+(BOOL)resolveClassMethod:(SEL)sel,如果有指定的动态解析方法则响应,否则继续<PS:resolveInstanceMethod解析对象方法;resolveClassMethod解析类方法>
     -->进入消息重定向 -(id)forwardingTargetForSelector:(SEL)aSelector;如果有指定消息接收对象则响应,否则继续
     -->开始消息转发 -(void)forwardInvocation:(NSInvocation *)anInvocation;如果有指定转发对象则响应,否则抛出异常
     *
     **/
    /// 下面我们着重实现动态解析的过程
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == NSSelectorFromString(@"run:"))
        {
            /// type: 解释 "s@:@" \
            /// s@:表示函数返回值;其中s表示返回字符串类型,v表示void,i表示int,以此类推;
            ///返回值类型后面的@表示参数,有几个参数就有几个@;
            class_addMethod(self, sel, (IMP)_c_method, "s@:@");
    
            return YES;
        }
    
        return [super resolveInstanceMethod:sel];
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        SEL name = [anInvocation selector];
        NSLog(@" >> forwardInvocation for selector %@", NSStringFromSelector(name));
        
        UsrModel * proxy = [[UsrModel alloc] init];
        if ([proxy respondsToSelector:name]) {
            [anInvocation invokeWithTarget:proxy];
        }
        else {
            [super forwardInvocation:anInvocation];
        }
    }
    
    +(BOOL)resolveClassMethod:(SEL)name
    {
        NSLog(@" >> Class resolving %@", NSStringFromSelector(name));
        return [super resolveClassMethod:name];
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        return [UsrModel instanceMethodSignatureForSelector:aSelector];
    }
    
    -(void)Bar
    {
        NSLog(@" >> Bar() in Foo.");
    }
    - (NSNumber *)eat
    {
        NSLog(@"我吃了");
        return @43;
    }
    
    @end
    

    代码中已附个人整理。
    此份总结参考于各个大牛的经验分享。在此,谢谢各位大牛的分享~
    最后 附demo: https://github.com/304164084/runtime_demo

    相关文章

      网友评论

          本文标题:runtime之动态添加方法

          本文链接:https://www.haomeiwen.com/subject/sgwlfftx.html