今天简单研究一下iOS的重定向消息,首先看看会用到的几个类:
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
官方说明:
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任意对象的任意方法了。
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 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"的方法。
那么,具体怎么执行呢?首先我们来看看正常情况一个消息(方法)执行的过程:
1. 发送消息如:[self startwork]
2. 系统会check是否能response这个消息
3. 如果能response则调用相应方法,不能则抛出异常
在第二步中,如果实例本身就有相应的response,那么就会响应之,如果没有系统就会发出methodSignatureForSelector消息,检测它这个消息是否有效?有效就会继续发出forwardInvocation消息,无效则返回nil。如果是nil就会crash。
有点小乱哈,我们来看个官方的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; }
运行的结果是:
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