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:¶m 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:¶m 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:¶m 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