今天简单研究一下iOS的重定向消息forwardInvocation:

  首先看看Invocation类:

@interface NSInvocation : NSObject {
@private
__strong void *_frame;
__strong void *_retdata;
id _signature;
id _container;
uint8_t _retainedArgs;
uint8_t _reserved[];
} + (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig; - (NSMethodSignature *)methodSignature; - (void)retainArguments;
- (BOOL)argumentsRetained; - (id)target;
- (void)setTarget:(id)target; - (SEL)selector;
- (void)setSelector:(SEL)selector; - (void)getReturnValue:(void *)retLoc;
- (void)setReturnValue:(void *)retLoc; - (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx; - (void)invoke;
- (void)invokeWithTarget:(id)target; @end

NSInvocation

  官方说明:

  An NSInvocation is an Objective-C message rendered static, that is, it is an action turned into an object. NSInvocation objects are used to store and forward messages between objects and between applications, primarily by NSTimer objects and the distributed objects system.

  我感觉,NSInvocation有点类似于java里的反射,它有一套完整的装备:target,selector,returnValue,ArgumentArray,有了它们,NSInvocation就可以动态的invoke任意对象的任意方法了。

  那么,具体什么时候消息会重定向呢?我们先来看看正常情况一个消息(方法)执行的过程:

    1. 发送消息如:[self startwork]

    2. 系统会check是否能response这个消息

    3. 如果能response则调用相应方法,不能则抛出异常

  在第二步中,如果实例本身就有相应的response,那么就会响应之,如果没有系统就会向实例对象发出methodSignatureForSelector消息,检测它这个消息是否有效?有效就会继续发出forwardInvocation消息,无效则返回nil。如果是nil就会crash。

  OK,我们知道消息重定向的执行流程了,我们再来看看什么时候会用到消息重定向。

  一. 模拟多继承

  重定向消息可以模拟多继承。一个对象已经继承一个父类了,还行执行其他类的方法,怎么办,我们可以考虑用重定向。

    

  如图,在特殊情况下,也有可能上一个战士去跟对方谈判,但是战士是用来扛枪打仗的,不会谈判啊,怎么办?于是战士向外交官借了几个锦囊,只要谈判的时候有搞不定的问题,就可以打开锦囊,发送forwardInvocation:重定向消息。

  二. 代理

  NSProxy类

NS_ROOT_CLASS
@interface NSProxy <NSObject> {
Class isa;
} + (id)alloc;
+ (id)allocWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (Class)class; - (void)forwardInvocation:(NSInvocation *)invocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel;
- (void)dealloc;
- (void)finalize;
- (NSString *)description;
- (NSString *)debugDescription;
+ (BOOL)respondsToSelector:(SEL)aSelector; - (BOOL)allowsWeakReference NS_UNAVAILABLE;
- (BOOL)retainWeakReference NS_UNAVAILABLE; // - (id)forwardingTargetForSelector:(SEL)aSelector; @end

NSProxy

  官方说明:

  NSProxy is an abstract superclass defining an API for objects that act as stand-ins for other objects or for objects that don’t exist yet. Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy can be used to implement transparent distributed messaging (for example, NSDistantObject) or for lazy instantiation of objects that are expensive to create.

  由此可见,这个代理类可以让一个实例执行它本身未定义的方法,它可以通过设置"real object"(通过子类定义)来让NSProxy执行"real object"的方法。

  有点小乱哈,我们来看个官方的demo,就明白了。

#import <Foundation/Foundation.h>
#include <stdio.h> @interface TargetProxy : NSProxy {
id realObject1;
id realObject2;
} - (id)initWithTarget1:(id)t1 target2:(id)t2; @end @implementation TargetProxy - (id)initWithTarget1:(id)t1 target2:(id)t2 {
realObject1 = [t1 retain];
realObject2 = [t2 retain];
return self;
} - (void)dealloc {
[realObject1 release];
[realObject2 release];
[super dealloc];
} // The compiler knows the types at the call site but unfortunately doesn't
// leave them around for us to use, so we must poke around and find the types
// so that the invocation can be initialized from the stack frame. // Here, we ask the two real objects, realObject1 first, for their method
// signatures, since we'll be forwarding the message to one or the other
// of them in -forwardInvocation:. If realObject1 returns a non-nil
// method signature, we use that, so in effect it has priority.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig;
sig = [realObject1 methodSignatureForSelector:aSelector];
if (sig) return sig;
sig = [realObject2 methodSignatureForSelector:aSelector];
return sig;
} // Invoke the invocation on whichever real object had a signature for it.
- (void)forwardInvocation:(NSInvocation *)invocation {
id target = [realObject1 methodSignatureForSelector:[invocation selector]] ? realObject1 : realObject2;
[invocation invokeWithTarget:target];
} // Override some of NSProxy's implementations to forward them...
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([realObject1 respondsToSelector:aSelector]) return YES;
if ([realObject2 respondsToSelector:aSelector]) return YES;
return NO;
} @end int main(int argc, const char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Create an empty mutable string, which will be one of the
// real objects for the proxy.
NSMutableString *string = [[NSMutableString alloc] init]; // Create an empty mutable array, which will be the other
// real object for the proxy.
NSMutableArray *array = [[NSMutableArray alloc] init]; // Create a proxy to wrap the real objects. This is rather
// artificial for the purposes of this example -- you'd rarely
// have a single proxy covering two objects. But it is possible.
id proxy = [[TargetProxy alloc] initWithTarget1:string target2:array]; // Note that we can't use appendFormat:, because vararg methods
// cannot be forwarded!
[proxy appendString:@"This "];
[proxy appendString:@"is "];
[proxy addObject:string];
[proxy appendString:@"a "];
[proxy appendString:@"test!"]; NSLog(@"count should be 1, it is: %d", [proxy count]); if ([[proxy objectAtIndex:] isEqualToString:@"This is a test!"]) {
NSLog(@"Appending successful.", proxy);
} else {
NSLog(@"Appending failed, got: '%@'", proxy);
} NSLog(@"Example finished without errors.");
[pool release];
return ;
}

Proxy Demo Code

  运行的结果是:

  count should be 1, it is:  1

  Appending successful.

  我的第一感觉是:好神奇!一个proxy对象既可以当NSString用,又可以当NSMultableArray用!

  那么我们什么时候用代理的重定向功能呢?官方说明里举了两个例子,实现透明分布式消息(如NSDistantObject)和懒加载非常耗时的对象。

  我到是想起了j2EE的SpringMVC里的模块视图,一个很NB的对象,可以解决Web请求的各种问题。也就是说,我们可以用代理来实现门面设计模式,即把很复杂的也无逻辑就交给一个对象来处理,我们做什么事情都跟这个代理对象打交道就可以了。

  最后说一下,消息重定向不是为了模仿多继承而设计的,如果可以,最好用常规方式解决问题,实在万不得已的时候再用。用的时候一定要搞清楚对象消息重定向的发送流程。

  reference:

  http://blog.csdn.net/devday/article/details/7418022

  https://developer.apple.com/library/mac/samplecode/ForwardInvocation/Listings/main_m.html#//apple_ref/doc/uid/DTS40008833-main_m-DontLinkElementID_4

iOS 代理 重定向消息 forwardInvocation的更多相关文章

  1. IOS 推送消息 php做推送服务端

    IOS推送消息是许多IOS应用都具备的功能,最近也在研究这个功能,参考了很多资料终于搞定了,下面就把步骤拿出来分享下: iOS消息推送的工作机制可以简单的用下图来概括: Provider是指某个iPh ...

  2. (转)在SAE使用Apple Push Notification Service服务开发iOS应用, 实现消息推送

    在SAE使用Apple Push Notification Service服务开发iOS应用, 实现消息推送 From: http://saeapns.sinaapp.com/doc.html 1,在 ...

  3. QF——iOS代理模式

    iOS的代理模式: A要完成某个功能,它可以自己完成,但有时出于一些原因,不方便自己完成.这时A可以委托B来帮其完成此功能,即由B代理完成.但是这个功能不是让B随随便便任其完成.此时,会有一个协议文件 ...

  4. 基于APNs最新HTTP/2接口实现iOS的高性能消息推送(服务端篇)

    1.前言 本文要分享的消息推送指的是当iOS端APP被关闭或者处于后台时,还能收到消息/信息/指令的能力. 这种在APP处于后台或关闭情况下的消息推送能力,通常在以下场景下非常有用: 1)IM即时通讯 ...

  5. iOS 发送位置消息

    发送地理位置在社交应用里面是很常用的需求.最近也需要在自己的应用里面加入这个功能 首先我们需要获取自己的地理位置,这里用到 CLLocationManager 这个类,调用如下代码 locationM ...

  6. iOS代理模式

    iOS代理模式的简单理解:当一个对象无法直接获取到另一个对象的指针,又希望对那个变量进行一些操作时,可以使用代理模式. 代理主要由三部分组成: (1)协议:用来指定代理双方可以做什么,必须做什么. ( ...

  7. 你真的了解iOS代理设计模式吗?

    在项目中我们经常会用到代理的设计模式,这是iOS中一种消息传递的方式,也可以通过这种方式来传递一些参数.这篇文章会涵盖代理的使用技巧和原理,以及代理的内存管理等方面的知识.我会通过这些方面的知识,带大 ...

  8. IOS 基于APNS消息推送原理与实现(JAVA后台)

    Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Pu ...

  9. 【转】你真的了解iOS代理设计模式吗?

    转自:http://www.cocoachina.com/ios/20160317/15696.html 在项目中我们经常会用到代理的设计模式,这是iOS中一种消息传递的方式,也可以通过这种方式来传递 ...

随机推荐

  1. 孤荷凌寒自学python第五十天第一次接触NoSql数据库_Firebase

    孤荷凌寒自学python第五十天第一次接触NoSql数据库_Firebase (完整学习过程屏幕记录视频地址在文末) 之前对关系型数据库的学习告一段落,虽然能力所限没有能够完全完成理想中的所有数据库操 ...

  2. 不作伪分享者决定完整分享我自学Python3的全部过程细节

    不作伪分享者决定完整分享我自学Python3的全部过程细节   我不要作伪分享者 十六年前我第一次见到了电脑,并深深地爱上了它: 十二年前我第一次连上了网络,并紧紧地被它爱上. 十年前的网络是田园美景 ...

  3. [转]LVS+Keepalived负载均衡配置

    简介 来源:https://www.cnblogs.com/MacoLee/p/5858995.html lvs一般是和keepalived一起组合使用的,虽然也可以单独使用lvs,但配置比较繁琐,且 ...

  4. pom中的resources设置

    Maven项目中一般都会把配置文件放到src/main/resources目录下,有时为了满足多个环境打包发布,可能会创建一些自定义目录来放置各环境的配置文件,如:src/main/profile/d ...

  5. hdu 1203 01背包 I need a offer

    hdu 1203  01背包  I need a offer 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1203 题目大意:给你每个学校得到offe ...

  6. Spring 事件机制

    通过模拟邮件的发送,说明Spring的事件监听机制 事件类 package org.zln.module_chapter2.event; import org.springframework.cont ...

  7. 【bzoj1706】[usaco2007 Nov]relays 奶牛接力跑 离散化+倍增Floyd

    题目描述 FJ的N(2 <= N <= 1,000,000)头奶牛选择了接力跑作为她们的日常锻炼项目.至于进行接力跑的地点 自然是在牧场中现有的T(2 <= T <= 100) ...

  8. BZOJ4652 [Noi2016]循环之美 【数论 + 莫比乌斯反演 + 杜教筛】

    题目链接 BZOJ 题解 orz 此题太优美了 我们令\(\frac{x}{y}\)为最简分数,则\(x \perp y\)即,\(gcd(x,y) = 1\) 先不管\(k\)进制,我们知道\(10 ...

  9. JavaScript各变量类型的判断方法

    我们很容易被漂亮的代码吸引,也不知不觉的在自己的代码库中加入这些.却没有冷静的想过它们的优劣.这不,我就收集了一系列形如 "是否为……?" 的判断的boolean函数. isNul ...

  10. 看angualrjs源码中怎么判断所属的类型

    下面是angualrjs的代码: function isFile(obj) { return toString.call(obj) === '[object File]'; } function is ...