iOS 代理 重定向消息 forwardInvocation

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

  首先看看Invocation类:

@interface NSInvocation : NSObject {
@private
    __strong void *_frame;
    __strong void *_retdata;
    id _signature;
    id _container;
    uint8_t _retainedArgs;
    uint8_t _reserved[15];
}

+ (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:0] isEqualToString:@"This is a test!"]) {
        NSLog(@"Appending successful.", proxy);
    } else {
        NSLog(@"Appending failed, got: '%@'", proxy);
    }
 
    NSLog(@"Example finished without errors.");
    [pool release];
    return 0;
}
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

转载于:https://www.cnblogs.com/treejohn/p/3596531.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于微信小程序的家政服务预约系统采用PHP语言和微信小程序技术,数据库采用Mysql,运行软件为微信开发者工具。本系统实现了管理员和客户、员工三个角色的功能。管理员的功能为客户管理、员工管理、家政服务管理、服务预约管理、员工风采管理、客户需求管理、接单管理等。客户的功能为查看家政服务进行预约和发布自己的需求以及管理预约信息和接单信息等。员工可以查看预约信息和进行接单。本系统实现了网上预约家政服务的流程化管理,可以帮助工作人员的管理工作和帮助客户查询家政服务的相关信息,改变了客户找家政服务的方式,提高了预约家政服务的效率。 本系统是针对网上预约家政服务开发的工作管理系统,包括到所有的工作内容。可以使网上预约家政服务的工作合理化和流程化。本系统包括手机端设计和电脑端设计,有界面和数据库。本系统的使用角色分为管理员和客户、员工三个身份。管理员可以管理系统里的所有信息。员工可以发布服务信息和查询客户的需求进行接单。客户可以发布需求和预约家政服务以及管理预约信息、接单信息。 本功能可以实现家政服务信息的查询和删除,管理员添加家政服务信息功能填写正确的信息就可以实现家政服务信息的添加,点击家政服务信息管理功能可以看到基于微信小程序的家政服务预约系统里所有家政服务的信息,在添加家政服务信息的界面里需要填写标题信息,当信息填写不正确就会造成家政服务信息添加失败。员工风采信息可以使客户更好的了解员工。员工风采信息管理的流程为,管理员点击员工风采信息管理功能,查看员工风采信息,点击员工风采信息添加功能,输入员工风采信息然后点击提交按钮就可以完成员工风采信息的添加。客户需求信息关系着客户的家政服务预约,管理员可以查询和修改客户需求信息,还可以查看客户需求的添加时间。接单信息属于本系统里的核心数据,管理员可以对接单的信息进行查询。本功能设计的目的可以使家政服务进行及时的安排。管理员可以查询员工信息,可以进行修改删除。 客户可以查看自己的预约和修改自己的资料并发布需求以及管理接单信息等。 在首页里可以看到管理员添加和管理的信息,客户可以在首页里进行家政服务的预约和公司介绍信息的了解。 员工可以查询客户需求进行接单以及管理家政服务信息和留言信息、收藏信息等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值