swizzle method 和消息转发机制的实际使用
我的工程结构,如图 1-0

图 1-0
在看具体实现以前,先捋以下 实现思路。
ViewController 中有一个-(void)Amethod;A方法。
-(void)Amethod{
    NSLog(@"Amethod");
}
1.swizzle method
在ViewController 的 -(void)viewDidLoad中调用 Amentohd;运行输出

图 1-1
创建一个ViewController 的Category,重写+(void)Load;方法(因为这个方法只会在类被加载时,调用一次。至于initialize,它们之间区别,参考 iOS - + initialize 与 +load),在实现-(void)Bmethod;
+(void)load{
     Class class = [self class];
    Method m3 =class_getInstanceMethod(class, @selector(Amethod));
    Method m4  =class_getInstanceMethod(class, @selector(Bmethod));
    method_exchangeImplementations(m3, m4);
}
-(void)Bmethod{
NSLog(@"swizzle method succuess :Bmenthod");
}
运行结果如下图1-2,

1-2
这就是一个简单的交换方法的思路,如果你认真的看了上面的 参考文章,就会发现+(void)Load;是在所有类加载的时候执行的, 那么如果在这个方法里,执行过多操作,是会推延APP启动时间的。所以不要在里面做过多的操作。
由于swizzle method is not atomic,不是线程安全的,所以你可以在dispatce_once 中完成swizzle。
+(void)load{
            Class class = [self class];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method m3 =class_getInstanceMethod(class, @selector(Amethod));
        Method m4  =class_getInstanceMethod(class, @selector(Bmethod));
/*下面代码的主要思路就是 主类有没有实现Amethod, 实现了,就交换Amenthod和Bmenthod,没实现就给她加上Amethod,但IMP和enconding type是Bmethod的。
 在加上Bmenthod,但IMP 和 encoding type是Amethod的。
*/ 
BOOL success = class_addMethod(class, method_getName(m3), method_getImplementation(m4), method_getTypeEncoding(m4));
if (success) {
class_replaceMethod(class, method_getName(m4), method_getImplementation(m3), method_getTypeEncoding(m3));
}else{
method_exchangeImplementations(m3, m4);
}
});
}
以上,就是 简单的swizzle method。你也可以在Bmethod 中再次调用Bmethod。不会出现递归。动动脑子就知道了。
2.消息转发 : 有三种方法是去补救实现相应方法。也可以做多继承使用。
我们在ViewController 中声明一个 button ,但是不实现它的绑定方法
UIButton *pushA = [[UIButton alloc]initWithFrame:CGRectMake(, , , )];
[pushA setTitle:@"pusha" forState:(UIControlStateNormal)];
[pushA addTarget:self action:@selector(pushA:) forControlEvents:(UIControlEventTouchUpInside)];
[self.view addSubview:pushA];
在它的分类中,实现下面代码,当一个实例方法没有实现就会调用 +(BOOL)resolveInstanceMethod:(SEL)sel;相对应对还有类方法+(BOOL)resolveClassMethod:(SEL)sel;
。
+(BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selectionString = NSStringFromSelector(sel);
    if ([selectionString isEqualToString:@"pushA:"]) {
        class_addMethod(self, sel, (IMP)pushA, "v@:*");// 在我之前关于YYModel的文中提到 Encoding type
    }
    return [super resolveInstanceMethod:sel];// 这里无论返回yes 还是NO ,如果方法没有实现都会调用第二种补救方式
}
//函数的具体实现 也是IMP
void pushA(id self,SEL sel,UIButton *sender){
    NSLog(@"动态添加方法%@",self);
    AViewController *vc = [AViewController new];
    AViewController *sourceVC = self;
    [sourceVC.navigationController pushViewController:vc animated:YES];
}
上面是第一种 补救方法。第二种就是-(id)forwardingTargetForSelector:(SEL)aSelector;
在ViewController 中在[self performSelector:@selector(Cmethod)];但不在Viewcontroller 中实现Cmenthod(关于 -(void)performSelector 还有很多方法,也会稍后添加)
在分类中,添加转发方法,
-(id)forwardingTargetForSelector:(SEL)aSelector{
    AViewController * avc = [AViewController new];
    if ([avc respondsToSelector:aSelector]) {
         return avc;
    }
    return [super forwardingTargetForSelector:aSelector];
}
在AViewController实现 -(void)Cmenthod;就完成消息转发给另一个对象。
第三种补救方式是- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;配合- (void)forwardInvocation:(NSInvocation *)anInvocation
第三种是一个完整的消息转发,但是开销很大。
在Viewcontroller 中的-(void)viewDidLoad 中调用
[self performSelector:@selector(changeBackgroundColor)];
目的是,改变视图的背景颜色(关于performSelector:系列,我会在单独总结)
在viewcontroller的分类中实现第三部补救方法,
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
//我现在是给是吧消息 传给 BViewController,并且追加了参数和返回值,修改了选择子
    NSString *selString = NSStringFromSelector(aSelector);
    if ([selString isEqualToString:@"changeBackgroundColor"]) {
        NSMethodSignature *sign = [[BViewController class] instanceMethodSignatureForSelector:@selector(changeBackgroundColor:)];
        sign = [NSMethodSignature signatureWithObjCTypes:"B@:*"];//参考 type enconding
        return sign;
    }
    return nil;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
NSString *selString = NSStringFromSelector(anInvocation.selector);
if ([selString isEqualToString:@"changeBackgroundColor"]) {
BViewController *vc = [BViewController new];
[anInvocation setTarget:vc]; // 设置target
[anInvocation setSelector:@selector(changeBackgroundColor:)]; //设置SEL
char * color1 = "r1111";
[anInvocation setArgument:&color1 atIndex:2]; //设置arg
bool returnValue = true;
[anInvocation setReturnValue:&returnValue]; //设置返回值为 true。(我的思路是 改变方法的返回值, 但实际上没有起到作用)
[anInvocation getReturnValue:&returnValue];// 获取返回值
NSLog(@"Return Value = %d", returnValue);// 此处打印返回值 结果却是为 true
[anInvocation retainArguments];// retain 一遍参数,防止被销毁
[anInvocation invoke];// 发送消息,还有一个-(void)invokeWithTarget:(id)target;方法是让taget对象接受消息
}else{
[super forwardInvocation:anInvocation];
}
}
在 BViewController 中实现 该 方法
-(bool)changeBackgroundColor:(char *)color{
    NSLog(@"改变背景颜色 %s %s ",__func__,color);
    return false;// 需要注意的是这里我设置了返回值是flase,而nsinvocation对象我设置的是true
}
运行得到

图 1-3
嗯, 消息成功转发了,但是我想修改返回值为ture,却没有成功。我设置返回值为true后,立即打印 确实是true,而动态调试的结果却是flase;如下图1-4

图 1-4
所以我将 ViewController+swizzle 中的 获取及打印returenValue 放在了, invoke 的后面,然而结果还是如此。如下图1-5

图 1-5
emmm,这种情况,我认为arg 既然需要retain,是不是返回值也需要retain,但是NSInvocation 的文档里没有 retain 方法。。。。我无计可施,带考证。。。。
swizzle method 和消息转发机制的实际使用的更多相关文章
- iOS的消息转发机制详解
		iOS开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法:或者,还有另外的方法,由于Objec ... 
- iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制
		你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639289 本文主要解说runtime相关知识, ... 
- 理解Objective-C Runtime(三)消息转发机制
		消息转发机制概述 上一篇博客消息传递机制中讲解了Objective-C中对象的「消息传递机制」.本文需要讲解另外一个重要问题:当对象受到无法处理的消息之后会发生什么情况? 显然,若想令类能理解某条消息 ... 
- iOS 消息转发机制
		这篇博客的前置知识点是 OC 的消息传递机制,如果你对此还不了解,请先学习之,再来看这篇.这篇博客我尝试用口语的方式像讲述 PPT 一样给大家讲述这个知识点. 我们来思考一个问题,如果对象在收到无法解 ... 
- Effective Objective-C 2.0 — 第12条:理解消息转发机制
		11 条讲解了对象的消息传递机制 12条讲解对象在收到无法解读的消息之后会发生什么,就会启动“消息转发”(message forwarding)机制, 若对象无法响应某个选择子,则进入消息转发流程. ... 
- iOS Runtime的消息转发机制
		前面我们已经讲解Runtime的基本概念和基本使用,如果大家对Runtime机制不是很了解,可以先看一下以前的博客,会对理解这篇博客有所帮助!!! Runtime基本概念:https://www.cn ... 
- iOS消息转发机制
		iOS消息转发机制 “消息派发系统”(message-dispatch system) 若想令类能够理解某条消息,我们必须实现出对应的方法才行.但是,在编译器向类发送其无法解读的消息时并不会报错,因为 ... 
- runtime消息转发机制
		Objective-C 扩展了 C 语言,并加入了面向对象特性和 Smalltalk 式的消息传递机制.而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库.它是 Objective- ... 
- OC:浅析Runtime中消息转发机制
		一.介绍 OC是一门动态性语言,其实现的本质是利用runtime机制.在runtime中,对象调用方法,其实就是给对象发送一个消息,也即objc_msgSend().在这个消息发送的过程中,系统会进行 ... 
随机推荐
- Linux向文件添加内容的几种方法
			例如,要想test.txt文件添加内容"I am a boy",test.txt在当前目录中 方法一:vi编辑法 打开终端,输入vi test.txt 回车,按a或i进入编辑模式, ... 
- iOS的异步绘制--YYAsyncLayer源码分析
			iOS的异步渲染 最近看了YYAsyncLayer在这里总结一下.YYAsyncLayer是整个YYKit异步渲染的基础.整个项目的Github地址在这里.你可以先下载了一睹为快,也可以跟着我一步一步 ... 
- ajax请求service报405错误 - 【服务器不允许的方法】
			产生原因:web服务器找不到service方法处理请求. 检查方向: ① service方法名称写错 ② service方法参数类型与标准不一致 ③ service方法异常,返回值类型和标准不一致 ④ ... 
- Python学习笔记(七)
			Python学习笔记(七): 深浅拷贝 Set-集合 函数 1. 深浅拷贝 1. 浅拷贝-多层嵌套只拷贝第一层 a = [[1,2],3,4] b = a.copy() print(b) # 结果:[ ... 
- UVa1605,Building for UN
			我比较好奇的是uva后台是怎么测这题的 没什么可说的,那些不想敲但还是想直接交这题的直接copy过去吧 #include <iostream> #include <cstring&g ... 
- LINUX 笔记-crontab命令
			用户所建立的crontab文件中,每一行都代表一项任务,每行的每个字段代表一项设置,它的格式共分为六个字段,前五段是时间设定段,第六段是要执行的命令段,格式如下: minute hour da ... 
- eclipse中project facet问题
			一般出现在从别处import的项目上,只有项目文件夹上有红叉,其他地方都正常,现总结个人的几个解决方案: 有几种可能: 1,编码设置是否一致,也即是你项目原来的编码和现在eclipse用的默认编码是否 ... 
- 2_ROS学习
			2_VNC远程连接树莓派 在上一次,我们成功的给树莓派安装了Ubuntu mate的操作系统. 树莓派是嵌入式计算机,一般是没有显示屏来显示的,我们通过远程连接来访问树莓派.网上推荐了ssh连接,xr ... 
- 【Salvation】——登录注册存储数据&验证用户
			写在前面:登录注册功能是在纯Unity3D环境内实现的,用到UGUI绘制界面技术,数据库的部分是后面拓展加进来的,这里数据存储是指存在XML用户文件中. 注册用户名和密码 zc() 用户名和密码登录 ... 
- Connect the Cities(prim)用prim都可能超时,交了20几发卡时过的
			Connect the Cities Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ... 
