runtime的使用一

2018-06-24  本文已影响10人  heart_领
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
上一篇 下一篇

猜你喜欢

热点阅读