美文网首页
runtime的使用一

runtime的使用一

作者: heart_领 | 来源:发表于2018-06-24 16:09 被阅读10次
    1529673472965.png

    1.消息发送机制

    #import "ViewController.h"
    #import "Person.h"
    #import <objc/message.h>
    @interface ViewController ()
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 1.第一种方式
        Person *per1 = [[Person alloc] init];
    //    [per1 run];
        // 伪代码
    //    objc_msgSend(per1, sel, 参数2,....);
    //    objc_msgSend(<#id  _Nullable self#>, <#SEL  _Nonnull op, ...#>)
        /**
         // 函数指针 (Person *(*)(id,SEL))是类型转换,id,SEL为参数类型,Person为返回类型,有点类似于block,这里是"*"block中是"^"
         Person *person = ((Person *(*)(id,SEL))objc_msgSend)((id)[Person class], @selector(alloc));
         person = ((Person *(*)(id,SEL))objc_msgSend)((id)person,@selector(init));
         ((Person *(*)(id,SEL))objc_msgSend)((id)person,@selector(run));
         //或者
         Person *person1 = ((id(*)(id,SEL))objc_msgSend)((id)[Person class], @selector(new));
         ((id(*)(id,SEL))objc_msgSend)((id)person1,@selector(run));
         //((void (*)(id,SEL))objc_msgSend)((id)person1,@selector(run));也可以运行
         */
    //    Person *person1 = ((id(*)(id,SEL))objc_msgSend)((id)[Person class], @selector(new));
    // ((id(*)(id,SEL))objc_msgSend)((id)person1,@selector(run));
    //    NSLog(@"哈哈哈哈:%d",jia);
        // 2.第二种方式
        [per1 performSelector:@selector(newMethod:string:) withObject:@"10" withObject:@"20"];
    }
    //函数指针,用来保存原始函数的地址.
    static id (*MYobjc_msgSend)(id _Nullable self, SEL _Nonnull op, ...);
    @end
    
    #import <Foundation/Foundation.h>
    @interface Person : NSObject
    - (void)run;
    @end
    
    #import "Person.h"
    #import <objc/runtime.h>
    @implementation Person
    - (void)run {
        NSLog(@"----跑起来");
    }
    // 解析实例方法
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        if (sel == @selector(newMethod:string:)) {
            // 运行时动态添加方法
            class_addMethod(self, @selector(newMethod:string:), (IMP)myMethodIMP, "v@:@@");
            /**
             https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
             */
        }
    
    /**
    run为oc方法
    if (sel == @selector(walk)) {
            Method runMethod = class_getInstanceMethod(self, @selector(run));
            IMP runIMP = method_getImplementation(runMethod);
            const char* types = method_getTypeEncoding(runMethod);
            NSLog(@"%s", types);
            return class_addMethod(self, sel, runIMP, types);
        }
    */
        return [super resolveInstanceMethod:sel];
    }
    // 实现函数 (c方法)
    void myMethodIMP(id self, SEL _cmd,NSString *new, NSString *str) {
        NSLog(@"打印的是performSelector的方法,第一个 = %@, 第二个 = %@",new,str);
    }
    @end
    

    2.消息转发机制

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        Person *person = [[Person alloc] init];
    //    [person run];
        [person run:@"100"];
    }
    
    #import <Foundation/Foundation.h>
    @interface Animation : NSObject
    - (void)run;
    - (void)run:(NSString *)string;
    @end
    
    #import "Animation.h"
    @implementation Animation
    - (void)run {
        NSLog(@"----动物跑");
    }
    - (void)run:(NSString *)string {
        NSLog(@"--%@-",string);
    }
    @end
    
    #import <Foundation/Foundation.h>
    @interface Person : NSObject
    - (void)run;
    - (void)run:(NSString *)string;
    @end
    
    // 快速消息转发
     - (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
     // 标准消息转发
     - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
     - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
     // 动态方法解析
     + (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
     + (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
     */
    #import "Person.h"
    #import <objc/runtime.h>
    #import "Animation.h"
    @implementation Person
    // 1. 解析实例方法
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        if (sel == @selector(run)) {
            // 运行时动态添加方法
    //        class_addMethod(self, sel, (IMP)runNew, "v@:");//如果添加了动态方法,就不会在走第二步
        }
        return [super resolveInstanceMethod:sel];//如果没有动态添加方法,无论返回YES还是NO,都会走第二步
    }
    // 实现函数
    void runNew(id self, SEL sel) {
        NSLog(@"runNew = %@--%@",self,NSStringFromSelector(sel));
    }
    // 2.快速消息转发
    - (id)forwardingTargetForSelector:(SEL)aSelector {
    //    NSLog(@"-----");
    //    return [[Animation alloc] init];//一旦转发成功不会再走第三步
        return [super forwardingTargetForSelector:aSelector];//如果没有转发,返回nil也会走第三步
    }
    // 3.标注消息转发 NSInvocation 实现了命令模式
    // 把消息包装成对象
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        // 1.拿到消息
        SEL selector = [anInvocation selector];
        // 2.转发出去
        Animation *anm = [[Animation alloc] init];
        if ([anm respondsToSelector:selector]) {
    //        anInvocation.target = anm;
    //        [anInvocation invoke];
            [anInvocation invokeWithTarget:anm];
        }
    }
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        NSString *sel = NSStringFromSelector(aSelector);
        if ([sel isEqualToString:@"run:"]) {
            // v是void类型   @是对象(哪个对象的哪个方法)  :是方法
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        } else {
        return [super methodSignatureForSelector:aSelector];
        }
    }
    @end
    

    3.消息转发机制模拟多继承

    #import <Foundation/Foundation.h>
    @protocol OneBaseClassProtocol
    - (void)oneBaseClassString:(NSString *)string;
    @end
    @interface OneBaseClass : NSObject <OneBaseClassProtocol>
    @end
    
    #import "OneBaseClass.h"
    @implementation OneBaseClass
    - (void)oneBaseClassString:(NSString *)string {
        NSLog(@"oneBaseClassString = %@",string);
    }
    @end
    
    #import <Foundation/Foundation.h>
    @protocol TwoBaseClassProtocol
    - (void)twoBaseClassString:(NSString *)string;
    @end
    @interface TwoBaseClass : NSObject <TwoBaseClassProtocol>
    @end
    
    /*
     1.接口继承过来
     2.消息转发间接调用实现
     3.类属性是没有继承
     */
    
    #import "TwoBaseClass.h"
    @implementation TwoBaseClass
    - (void)twoBaseClassString:(NSString *)string {
        NSLog(@"twoBaseClassString = %@",string);
    }
    @end
    
    #import <Foundation/Foundation.h>
    #import "OneBaseClass.h"
    #import "TwoBaseClass.h"
    @interface SubClass : NSProxy <OneBaseClassProtocol,TwoBaseClassProtocol>
    + (instancetype)subClassMethod;
    @end
    
    #import "SubClass.h"
    #import <objc/runtime.h>
    @interface SubClass () {
        NSMutableDictionary *_methodsMap;//用于存储类中的方法,value为对像,key为方法
    }
    @end
    @implementation SubClass
    + (instancetype)subClassMethod {
        return [[SubClass alloc] init];
    }
    - (instancetype)init {
        _methodsMap = [NSMutableDictionary dictionary];
        // 添加方法  用运行时拿到类里面的方法列表
        [self registerMethodsWithTarget:[OneBaseClass new]];
        [self registerMethodsWithTarget:[TwoBaseClass new]];
        return self;
    }
    - (void)registerMethodsWithTarget:(id)target {
        unsigned int count = 0;
        // 拿到类里面的方法列表
        Method *methodList = class_copyMethodList([target class], &count);
        // 依次拿出来,添加到字典
        for (int i = 0; i < count; ++i) {
            // 方法列表里面的具体方法
            Method method = methodList[i];
            SEL sel = method_getName(method);
            [_methodsMap setObject:target forKey:NSStringFromSelector(sel)];
        }
        // delloc 不会提醒
        free(methodList);
    }
    // 通过消息转发, 去实现方法
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
        // 1.方法名
        NSString *methodName = NSStringFromSelector(sel);
        // 2.方法目标对象. 类
        id target = _methodsMap[methodName];//找到方法对应的对象
        if (target && [target respondsToSelector:sel]) {
            return [target methodSignatureForSelector:sel];
    //        [NSMethodSignature signatureWithObjCTypes:"v@:"]
        } else {
            return [super methodSignatureForSelector:sel];
        }
    }
    //把消息转发出去
    - (void)forwardInvocation:(NSInvocation *)invocation {
        // 拿方法
        SEL sel = invocation.selector;
        NSString *methodName = NSStringFromSelector(sel);
        id target = _methodsMap[methodName];
        if (target && [target respondsToSelector:sel]) {
            // 执行
            [invocation invokeWithTarget:target];
        } else {
            [super forwardInvocation:invocation];
        }
    }
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        SubClass *subclass = [SubClass subClassMethod];
        [subclass oneBaseClassString:@"zhangsan"];
        [subclass twoBaseClassString:@"lisi"];
    }
    

    4.命令模式在路由中的使用

    #import <Foundation/Foundation.h>
    @interface OCTarget_show : NSObject
    - (id)action_name:(NSDictionary *)param;
    - (NSUInteger)action_home:(NSDictionary *)param;
    - (void)notFoundActionMethod:(NSDictionary *)parma;
    @end
    
    #import "OCTarget_show.h"
    @implementation OCTarget_show
    - (id)action_name:(NSDictionary *)param {
        NSLog(@"参数---%@-",param);
        return @(123);
    }
    - (void)notFoundActionMethod:(NSDictionary *)parma {
        NSLog(@"----cuo le");
    }
    - (NSUInteger)action_home:(NSDictionary *)param {
        if (param) {
            NSLog(@"参数---%@-",param);
            return 1;
        } else {
            NSLog(@"没有参数");
            return 0;
        }
    }
    @end
    
    #import <Foundation/Foundation.h>
    @interface OCRouter : NSObject
    + (instancetype)sharedInstance;
    // 目的: 根据target-action去找对应的 类里面的方法
    - (id)perforTarget:(NSString *)targetName actionName:(NSString *)actionName param:(NSDictionary *)param;
    @end
    
    #import "OCRouter.h"
    @implementation OCRouter
    + (instancetype)sharedInstance
    {
        static OCRouter *mediator;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            mediator = [[OCRouter alloc] init];
        });
        return mediator;
    }
    // 可以通过传入的字符串, 去找对应类
    - (id)perforTarget:(NSString *)targetName actionName:(NSString *)actionName param:(NSDictionary *)param {
        // 1.拼接规范
        NSString *targetClassName = [NSString stringWithFormat:@"OCTarget_%@",targetName];//获取类名
        NSString *actionMethodName = [NSString stringWithFormat:@"action_%@:",actionName];//获取方法名
        // 2.装化成类以及方法
        Class targetClass = NSClassFromString(targetClassName);//获得类
        SEL action = NSSelectorFromString(actionMethodName);//获得方法
        NSObject *target =  [[targetClass alloc] init];//初始化类
        // 3.判断响应跳转
        if (target && [target respondsToSelector:action]) {
            // 执行代码
    //      return  [target performSelector:action withObject:target withObject:param];
            return [self safePerformSelector:target actionName:action param:param];
        } else {
            // 找对应的容错提示
            SEL action = NSSelectorFromString(@"notFoundActionMethod:");
            if (target&&[target respondsToSelector:action]) {
                // 执行代码
                return [self safePerformSelector:target actionName:action param:param];
            } else {
                return nil;
            }
        }
    }
    // 如果是基本数据类型,就要改.如果是多个参数 NSInvcation   void  NSUIIntege Bool
    - (id)safePerformSelector:(NSObject *)target actionName:(SEL)action param:(NSDictionary *)param {
        // 1.拿到方法签名
        NSMethodSignature *methodSig = [target methodSignatureForSelector:action];
        if (methodSig == nil) {
            NSString *info = [NSString stringWithFormat:@"%@方法找不到", NSStringFromSelector(action)];
            [NSException raise:@"方法调用出现异常" format:info, nil];
        }
        // 2.获取方法返回类型
        const char *returnType = [methodSig methodReturnType];
        // 3.  void  NSUIInteger Bool NSInteger
    //    strcmp(returnType, @encode(void))判断返回类型
        if (strcmp(returnType, @encode(void)) == 0) {//返回类型为void
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
            // 一个参数和第二个参数是固定的.为self和_cmd
            [invocation setArgument:&param atIndex:2];//从第三个位置开始设置参数
            [invocation setTarget:target];
    //        [invocation setSelector:action];//可以在此处修改调用的方法,把action方法换成其他的方法
            // 执行
            [invocation invoke];
            // 返回空
            return nil;
        }
        // 3.  void  NSUIInteger Bool NSInteger
        if (strcmp(returnType, @encode(NSUInteger)) == 0) {//返回类型为NSUInteger
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
            // 一个参数和第二个参数是固定的.
            [invocation setArgument:&param atIndex:2];
            [invocation setTarget:target];
            [invocation setSelector:action];
            // 执行
            [invocation invoke];
            // 返回需要的类型
            NSInteger result = 0;
            [invocation getReturnValue:&result];
            return @(result);
        }
        if (strcmp(returnType, @encode(id)) == 0) {//返回类型为NSInteger
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
            // 一个参数和第二个参数是固定的.
            [invocation setArgument:&param atIndex:2];
            [invocation setTarget:target];
            [invocation setSelector:action];
            // 执行
            [invocation invoke];
            // 返回需要的类型
            id result;
            [invocation getReturnValue:&result];
            return result;
        }
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        //withObject:target 是把它当参数传的, 就多了个参数
        //    return  [target performSelector:action withObject:target withObject:param];
         return [target performSelector:action  withObject:param];
    #pragma clang diagnostic pop
    }
    @end
    
    #import "ViewController.h"
    #import "OCRouter.h"
    @interface ViewController ()
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        NSString *string = [[OCRouter sharedInstance] perforTarget:@"show" actionName:@"name" param:nil];
        NSString *string2 = [[OCRouter sharedInstance] perforTarget:@"show" actionName:@"name" param:@{@"name":@"dayin"}];
        NSLog(@"-----%@----",string);
        NSLog(@"-----%@----",string2);
        //
        [[OCRouter sharedInstance] perforTarget:@"show" actionName:@"home" param:@{@"name":@"zhang"}];
        [[OCRouter sharedInstance] perforTarget:@"show" actionName:@"home" param:nil];
    }
    @end
    

    相关文章

      网友评论

          本文标题:runtime的使用一

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