一、 使用方法签名和调用
1,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.
An NSInvocation object contains all the elements of an Objective-C message: a target, a selector, arguments, and the return value. Each of these elements can be set directly, and the return value is set automatically when the NSInvocation object is dispatched.
An NSInvocation object can be repeatedly dispatched to different targets; its arguments can be modified between dispatch for varying results; even its selector can be changed to another with the same method signature (argument and return types). This flexibility makes NSInvocation useful for repeating messages with many arguments and variations; rather than retyping a slightly different expression for each message, you modify the NSInvocation object as needed each time before dispatching it to a new target.
NSInvocation does not support invocations of methods with either variable numbers of arguments or union arguments. You should use the invocationWithMethodSignature: class method to create NSInvocation objects; you should not create these objects using alloc and init.
This class does not retain the arguments for the contained invocation by default. If those objects might disappear between the time you create your instance of NSInvocation and the time you use it, you should explicitly retain the objects yourself or invoke the retainArguments method to have the invocation object retain them itself.
这是文档中给的解释。
2,简单的demo。
SEL initSel = @selector(init);
SEL allocSel = @selector(alloc);
NSMethodSignature *initSig, *allocSig;
//从实例中请求实例方法签名
initSig = [@"String" methodSignatureForSelector:initSel];
//从类中请求实例方法签名
initSig = [NSString instanceMethodSignatureForSelector:initSel];
//从类中请求实例方法签名
allocSig = [NSString methodSignatureForSelector:allocSel];
总结:从实例中与从类中获取到的方法签名是相同的。
NSMutableSet *set = [NSMutableSet set];
NSString *stuff = @"Stuff";
SEL selector = @selector(addObject:);
NSMethodSignature *sig = [set methodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:set];
[invocation setSelector:selector];
[invocation setArgument:&stuff atIndex:2];
[invocation invoke];
3,蹦床
蹦床把一条消息从一个对象“反弹”到里一个对象。这种技术允许一个代理对象把消息转移到另一个线程、缓存结果、合并重复的消息或者任何其他中间配置。蹦床一般使用forwardInvocation方法处理消息。如果一个对象在Objective-C提示错误之前不响应一个选择器,它就会创建一个NSInvocation并且传递给该对象的forwardInvocation:方法。你可以用它以任何方式转发消息。在下面这个示例中,你将创建一个叫RNObserverManager的蹦床。任何发送到trampoline(蹦床)的信息都将被转发到响应选择器的已注册的观察者。
我的理解:就是发送到蹦床的方法,蹦床都会找已经注册的观察者是不是可以响应发送到蹦床的方法,如果响应了就执行,也就是有100个观察者,可能同时100个观察者调用这个方法(上面的话这句话我理解了好久),实现了类似于通知的功能,当观察者较多的时候速度比较快,个人认为可以理解下这样的思路,在实际应用中并不常见,以下是蹦床代码。
// RNObserverManager.h
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface RNObserverManager : NSObject
- (id)initWithProtocol:(Protocol *)protocol observers:(NSSet *)observers;
- (void)addObserver:(id)observer;
- (void)removerObserber:(id)observer;
@end
//RNObserverManager.m
#import "RNObserverManager.h"
@interface RNObserverManager()
@property(nonatomic,readonly,strong)NSMutableSet *observers;
@property(nonatomic,readonly,strong)Protocol *protocol;
@end
@implementation RNObserverManager
- (id)initWithProtocol:(Protocol *)protocol observers:(NSSet *)observers{
if (self = [super init]) {
_protocol = protocol;
_observers = [NSMutableSet setWithSet:observers];
}
return self;
}
- (void)addObserver:(id)observer{
NSAssert([observer conformsToProtocol:self.protocol],@"Observer must conform to protocol.");
[self.observers addObject:observer];
}
- (void)removerObserber:(id)observer{
[self.observers removeObject:observer];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
//检查蹦床本身
NSMethodSignature *result = [super methodSignatureForSelector:aSelector];
if (result) {
return result;
}
//查找所需方法
struct objc_method_description desc = protocol_getMethodDescription(self.protocol, aSelector, YES, YES);
if (desc.name == NULL) {
//找不到 也许他是可选的
desc = protocol_getMethodDescription(self.protocol, aSelector, NO, YES);
}
if (desc.name == NULL) {
//找不到,抛出异常NSInvalidArgumentException
[self doesNotRecognizeSelector:aSelector];
return nil;
}
return [NSMethodSignature signatureWithObjCTypes:desc.types];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL selector = [anInvocation selector];
for (id responder in self.observers) {
if ([responder respondsToSelector:selector]) {
[anInvocation setTarget:responder];
[anInvocation invoke];
}
}
}
@end
蹦床的使用1:
//ViewController.h
#import <UIKit/UIKit.h>
#import "RNObserverManager.h"
@protocol MyProtocol <NSObject>
- (void)doSomething;
@end
@interface ViewController : UIViewController<MyProtocol>
@property(nonatomic,strong)NSMutableSet *observers;
@end
//ViewController.m
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_observers = [NSMutableSet set];
[_observers addObject:self];
id observerManager = [[RNObserverManager alloc]initWithProtocol:@protocol(MyProtocol) observers:_observers];
[observerManager doSomething];
}
- (void)doSomething{
NSLog(@"doSomthing");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
蹦床的使用2:
// RNMainThreadTrampoline.h
#import <Foundation/Foundation.h>
@interface RNMainThreadTrampoline : NSObject
@property(nonatomic,readwrite,strong)id target;
- (id)initWithTarget:(id)atarget;
@end
// RNMainThreadTrampoline.m
#import "RNMainThreadTrampoline.h"
@implementation RNMainThreadTrampoline
-(id)initWithTarget:(id)atarget{
if (self = [super init]) {
_target = atarget;
}
return self;
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [self.target methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation setTarget:self.target];
[anInvocation retainArguments];
[anInvocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:NO];
}
@end
解释:forwardInvocation:方法可以自如地合并重复消息、添加记录、将消息转发到其他机器,并且执行多种功能。
问题:这是一个可以将所有消息转发给主线程的蹦床,那具体的使用场景是什么呢?