runtime之动态添加方法

2018-03-06  本文已影响0人  Just丶Go
  实际上是解释消息传递的机制中的一个动态解析过程(新增)

需求: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

上一篇下一篇

猜你喜欢

热点阅读