ios 消息跳转处理
一.消息转发流程
当向Objective-C对象发送一个消息,但runtime在当前类及父类中找不到此selector对应的方法时,消息转发(message forwarding)流程开始启动。
- 动态方法解析(Dynamic Method Resolution或Lazy method resolution)
向当前类(Class)发送resolveInstanceMethod:(对于类方法则为resolveClassMethod:)消息,如果返回YES,则系统认为请求的方法已经加入到了,则会重新发送消息。 - 快速转发路径(Fast forwarding path)
若果当前target实现了forwardingTargetForSelector:方法,则调用此方法。如果此方法返回除nil和self的其他对象,则向返回对象重新发送消息。 - 慢速转发路径(Normal forwarding path)
首先runtime发送methodSignatureForSelector:消息查看Selector对应的方法签名,即参数与返回值的类型信息。如果有方法签名返回,runtime则根据方法签名创建描述该消息的NSInvocation,向当前对象发送forwardInvocation:消息,以创建的NSInvocation对象作为参数;若methodSignatureForSelector:无方法签名返回,则向当前对象发送doesNotRecognizeSelector:消息,程序抛出异常退出。Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MessageInterceptor test]: unrecognized selector sent to instance 0x9589830'
二.动态解析(Lazy Resolution)
runtime发送消息的流程即查找该消息对应的方法或IMP,然后跳转至对应的IMP。有时候我们不想事先在类中设置好方法,而想在运行时动态的在类中插入IMP。这种方法是真正的快速”转发”,因为一旦对应的方法被添加到类中,后续的方法调用就是正常的消息发送流程。此方法的缺点是不够灵活,你必须有此方法的实现(IMP),这意味这你必须事先预测此方法的参数和返回值类型。
@dynamic属性是使用动态解析的一个例子,@dynamic告诉编译器该属性对应的getter或setter方法会在运行时提供,所以编译器不会出现warning; 然后实现resolveInstanceMethod:方法在运行时将属性相关的方法加入到Class中。
当respondsToSelector:或instancesRespondToSelector:方法被调用时,若该方法在类中未实现,动态方法解析器也会被调用,这时可向类中增加IMP,并返回YES,则对应的respondsToSelector:的方法也返回YES。
三.快速转发(Fast Forwarding)
runtime然后会检查你是否想将此消息不做改动的转发给另外一个对象,这是比较常见的消息转发情形,可以用较小的消耗完成。
快速转发技术可以用来实现伪多继承,你只需编写如下代码
- (id)forwardingTargetForSelector:(SEL)sel { return _otherObject; }
这样做会将任何位置的消息都转发给_otherObject对象,尽管当前对象与_otherObject对象是包含关系,但从外界看来当前对象和_otherObject像是同一个对象。
伪多继承与真正的多继承的区别在于,真正的多继承是将多个类的功能组合到一个对象中,而消息转发实现的伪多继承,对应的功能仍然分布在多个对象中,但是将多个对象的区别对消息发送者透明。
四.慢速转发(Normal Forwarding)
以上两者方式是对消息转发的优化,如果你不使用上述两种方式,则会进入完整的消息转发流程。这会创建一个NSInvocation对象来完全包含发送的消息,其中包括target,selector,所有的参数,返回值。
在runtime构建NSInvocation之前首先需要一个NSMethodSignature,所以它通过-methodSignatureForSelector:方法请求。一旦NSInvocation创建完成,runtime就会调用forwardInvocation:方法,在此方法内你可以使用参数中的invocation做任何事情。无限可能…
举个例子,如果你想对一个NSArray中的所有对象调用同一个方法,而又不想一直写循环代码时,想直接操作NSArray时,可这样处理:
@implementation NSArray (ForwardingIteration)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
NSMethodSignature *sig = [super methodSignatureForSelector:sel];
if(!sig)
{
for(id obj in self)
if((sig = [obj methodSignatureForSelector:sel]))
break;
}
return sig;
}
- (void)forwardInvocation:(NSInvocation *)inv
{
for(id obj in self)
[inv invokeWithTarget:obj];
}
@end
然后就可以这样使用
[(NSWindow *)windowsArray setHidesOnDeactivate:YES];
不过不建议这样使用,因为若NSArray实现了此方法,就不会进入转发流程。实现这种功能的一种比较好的方法是使用NSProxy。
五.方法声明
虽然上述机制可以转发当前类中没有实现的方法,但发送消息时仍然需要知道每个消息的方法签名,否则就会有编译器告警。可以通过category来声明转发消息的方法。
六.使用消息转发在子类中处理Delegate消息
当继承一个具有delgate的类,而又需要在子类中处理某些delegate消息,而又不影响对正常Delegate消息的调用时,需要如何处理呢?
一种方法是将子类对象设为自身的delegate,而将外部设置的delegate存储到另一个参数中。在子类中实现所有的delegate方法,处理子类中需要处理的delegate消息,而将子类中不处理的delegate消息再发送到外部delegate。这种方法的缺点在于实现繁琐,在子类中需要实现所有delegate方法,尽管大部分delegate消息又直接转给了外部delegate处理。
另一种比较优雅的方式是使用消息转发,创建一个proxy类,将proxy类设置为父类的delegate,在proxy中分别将消息转发给子类或外部Delegate。
比如,创建一个UISCrollView的子类可使用如下代码
MessageInterceptor.h
@interface MessageInterceptor : NSObject {
id receiver;
id middleMan;
}
@property (nonatomic, assign) id receiver;
@property (nonatomic, assign) id middleMan;
@end
MessageInterceptor.m
@implementation MessageInterceptor
@synthesize receiver;
@synthesize middleMan;
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([middleMan respondsToSelector:aSelector]) { return middleMan; }
if ([receiver respondsToSelector:aSelector]) { return receiver; }
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([middleMan respondsToSelector:aSelector]) { return YES; }
if ([receiver respondsToSelector:aSelector]) { return YES; }
return [super respondsToSelector:aSelector];
}
@end
MyScrollView.h
#import "MessageInterceptor.h"
@interface MyScrollView : UIScrollView {
MessageInterceptor * delegate_interceptor;
//...
}
//...
@end
MyScrollView.m
@implementation MyScrollView
- (id)delegate { return delegate_interceptor.receiver; }
- (void)setDelegate:(id)newDelegate {
[super setDelegate:nil];
[delegate_interceptor setReceiver:newDelegate];
[super setDelegate:(id)delegate_interceptor];
}
- (id)init* {
//...
delegate_interceptor = [[MessageInterceptor alloc] init];
[delegate_interceptor setMiddleMan:self];
[super setDelegate:(id)delegate_interceptor];
//...
}
- (void)dealloc {
//...
[delegate_interceptor release];
//...
}
// delegate method override:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// 1. your custom code goes here
// 2. forward to the delegate as usual
if ([self.delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
[self.delegate scrollViewDidScroll:scrollView];
}
}
@end
MessageInterceptor对象会自动将将子类中实现的delegate消息转发给子类,而将其他所有delegate消息转发给外部设置的delegate对象。
在MessageInterceptor中除了实现forwardingTargetForSelector:方法外,还实现了respondsToSelector:方法,因为UIScrollView在发送delegate消息之前会首先使用respondsToSelector:判断delegate是否实现了该方法,而转发的消息对respondsToSelector:也应返回YES。
参考:
Friday Q&A 2009-03-27: Objective-C Message Forwarding
Objective-C Runtime Programming Guide – Dynamic Method Resolution
Objective-C Runtime Programming Guide – Message Forwarding
Intercept obj-c delegate messages within a subclass
Hacking Block Support Into UIMenuItem
NSObject Class Reference
NSObject Protocol Reference
NSInvocation Class Reference
NSMethodSignature Class Reference
ios 消息跳转处理的更多相关文章
- ios消息的交互方式
注意这些都是界面回传(即从第二个界面传到第一个界面,从第一个界面传到第二个界面的时候用第二个界面的属性即可) iOS消息的交互方式有4种,分别为:通知,代理,block,kvo 现在我们对这个4中 ...
- 了解iOS消息推送一文就够:史上最全iOS Push技术详解
本文作者:陈裕发, 腾讯系统测试工程师,由腾讯WeTest整理发表. 1.引言 开发iOS系统中的Push推送,通常有以下3种情况: 1)在线Push:比如QQ.微信等IM界面处于前台时,聊天消息和指 ...
- iOS页面跳转及数据传递
转: http://blog.csdn.net/wang9834664/article/details/8025571 iOS页面跳转: 第一种 [self.navigationController ...
- iOS 10 跳转系统设置
苦心人天不负, 为了项目终于把 iOS 10 跳转系统设置的方法给搞定了, 很欣慰. http://www.cnblogs.com/lurenq/p/6189580.html iOS 10 跳转系统设 ...
- IOS 消息机制(NSNotificationCenter)
消息机制 NSNotificationCenter 一直都在频繁使用,但是却对其原理不是十分了解.今天就花些时间,把消息机制原理重头到尾好好过一遍. iOS 提供了一种 "同步的" ...
- iOS界面跳转的一些优化方案
原文地址: http://blog.startry.com/2016/02/14/Think-Of-UIViewController-Switch/ iOS界面跳转的一些优化方案 App应用程序开发, ...
- iOS 消息推送(APNs) 傻瓜式教程
也可以去我的简书页面查看这篇文章 首先: 1.做iOS消息推送需要真机测试 2.做iOS消息推送需要有付费的开发者账号 是否继续看帖? 先学习一下相关的知识吧! 因为中途可能会遇到一些问题,这篇文章或 ...
- Ios 消息推送
手把手教你做iOS推送 http://www.cocoachina.com/industry/20130321/5862.html http://www.cnblogs.com/cdts_change ...
- (转)iOS消息推送机制的实现
原:http://www.cnblogs.com/qq78292959/archive/2012/07/16/2593651.html iOS消息推送机制的实现 iOS消息推送的工作机制可以简单的用下 ...
随机推荐
- HDU-1026 Ignatius and the Princess I(BFS) 带路径的广搜
此题需要时间更少,控制时间很要,这个题目要多多看, Ignatius and the Princess I Time Limit: 2000/1000 MS (Java/Others) Me ...
- 简单的cocos2d-x手势(转)
项目需要用到非常简单手势拨动,就是向上/下/左.右滑动时,界面能响应. 以下提供一个较为简单的手势滑动解决办法 GestureLayer.h class GestureLayer: public CC ...
- [CODEVS1216]跳马问题
题目描述 Description 题目 输入描述 Input Description 第一行两个正整数M,N(0<M,N≤300)分别表示行和列 第二行两个正整数,表示起点的行列坐标. 第三行两 ...
- sunlime操作
ctrl+p 查找文件 @进行符号查找ctrl+h 替换文件 选中字符以后多次按 ctrl+D 跳过则按 ctrl+Kctrl+shift+d 复制多行alt+f3 多选 ctrl+shift+p ...
- 应用引擎BAE3.0介绍及百度BAE3.0支持并发多少
百度云BAE3.0的特点:1.支持本地程序迁移百度云应用引擎BAE3.0做了很多的改进,其实就是一句话,百度云开发在不断的进步.为了节省开发者的学习成本,百度云BAE3.0增加了轻量级虚拟机,使开发环 ...
- 冒泡算法(C++模板实现)
冒泡排序 从整体上看,冒泡排序是一种稳定排序,即排序完成后,原本序列中的键值相等的元素相对位置不会发生改变.算法的时间复杂度是O(n2),空间复杂度为O(1),即这是一个"就地算法" ...
- 第4章 类型基础 -- 4.1 所有类型都从System.Object派生
4.1 所有类型都从System.Object派生 “运行时”要求每个类型最终都从System.Object类型派生. 由于所有类型最终都从System.Object派生,所以每个类型的每个对象都保证 ...
- Dllimport函数時无法在Dll中找到的入口点
今天開發客戶提供的一個dll時出現無法找到入口點問題,由於客戶也不能明確說明dll,所以一時不知道如何下手,經查詢後找到可通過vs自帶的dumpbin.exe查看. Dumpbin.exe位于 VS的 ...
- hdoj 1702 ACboy needs your help again!【数组模拟+STL实现】
ACboy needs your help again! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ( ...
- win8 64位系统,安装JDK的步骤及其环境配置
工具/原料 jdk-8u51-windows-x64.exe 下载地址:::http://www.cr173.com/soft/55503.html#address jdk 安装步骤 1:到oracl ...