NSInvocation 简介 在上篇文章关于消息的转发中介绍了,通过方法签名NSMethodSignature产生NSInvocation,然后配置NSInvocation 那么NSInvocation到底是什么呢,他在OC中扮演什么角色呢? 先感性的定义一个这个类,其实NSInvocation就是一个创建方法(消息),将方法具体化的一个类,NSInvocation能设置Target,参数,返回值和方法名称。 然后我们需要理性的看代码了如下: @interface NSInvocation : NSObject { @private __strong void *_frame; __strong 应用 NSInvocation可以去构建一个方法然后转发这个方法,所以我们可以利用这两点特性去做一些事情。
我们可以用NSInvocation的特性来达到这个目的 3、NSInvocation 我们知道,iOS的方法调用实际上就是消息转发 objc_msgSend(Class receiver,SEL selector 而NSInvocation就是用来制造消息发送的类。 NSInvocation与其他NSObject类不一样,不会通过alloc/init来生成,它需要通过一个方法签名NSMethodSignature来生成 NSInvocation *invocatin = [NSInvocation invocationWithMethodSignature:sig]; 而NSMethodSignature由类的selector来形成的 NSMethodSignature * invocatin = [NSInvocation invocationWithMethodSignature:sig]; [invocatin setTarget:obj]; [
背景 NSInvocation是iOS开发中常见的用来实现反射的方法,即通过传入方法名和参数等格式化的字符串后,即可调用指定的方法,虽然牺牲了运行性能,但是对于模块解耦确实是个杀手锏,而NSInvocation 问题 当我们需要获取NSInvocation调用方法的返回值时,会使用系统提供的- (void)getReturnValue:(id *retValue);方法,调用的代码大概如下面所示: NSMethodSignature *methodSignature = [AClass instanceMethodSignatureForSelector:NSSelectorFromString(action)]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [invocation setTarget:aInstance 这时机智的你肯定会拿出Zombie Object工具,这工具确实很有用,很快我们就可以定位出过度释放是发生在action方法调用过程中,经过一轮查证,问题最有可能就是出现在NSInvocation调用过程中了
但是perform相关的这些函数,有一个局限性,其参数数量不能超过2个,否则要做很麻烦的处理,与之相对,NSInvocation也是一种消息调用的方法,并且它的参数没有限制。 * invocatin = [NSInvocation invocationWithMethodSignature:sig]; //设置target [invocatin setTarget * invocatin = [NSInvocation invocationWithMethodSignature:sig]; [invocatin setTarget:self]; 二、NSInvocation的返回值 NSInvocation对象,是可以有返回值的,然而这个返回值,并不是其所调用函数的返回值,需要我们手动设置: - (void)viewDidLoad { * invocatin = [NSInvocation invocationWithMethodSignature:sig]; [invocatin setTarget:self];
前言 在认识 NSInvocation 之前,iOS开发中我们一般会使用以下两种方式去调用一个方法 (1) [obj methodName]; (2) [obj performSelector... 答案肯定是有的,那就是 NSInvocation 。NSInvocation可以处理很多参数、返回值。会java的人都知道反射操作,其实NSInvocation就相当于反射操作。 NSInvocation 文档描述 An NSInvocation is an Objective-C message rendered static, that is, it is an action NSInvocation可以去构建一个方法然后转发这个方法,所以我们可以利用这两点特性去做一些事情。 的使用步骤 (1).指定一个 SEL (2).根据这个 SEL 创建 NSMethodSignature (3).根据这个 NSInvocation 创建一个NSInvocation对象 (4)
NSInvocation. 第一个PerformaceSelector比较常用, 也比较简单。 但是这个方式最多只能传递2个参数 当需要2个以上参数时就只能用NSInvocation了 直接上代码吧, 会注释清楚 - (void)viewDidLoad { [super viewDidLoad ViewController instanceMethodSignatureForSelector:@selector(printStr1:Str2:Str3:)]; //2.根据方法签名来创建NSInvocation 对象, 在这之前最好先判断下前面创建的signature是否为nil, 方法不存时就是nil NSInvocation *invocation = [NSInvocation invocationWithMethodSignature BezierPathDemo[1203:97184] Second argument 2017-01-06 11:55:07.399 BezierPathDemo[1203:97184] Third argument NSInvocation
不能获取NSInvocation消息相关内容。 不能操作处理参数 不能处理返回值 只是简单的返回一个对象来处理未知方法。 第三步 、给对象发送一个消息-(void)forwardInvocation:(NSInvocation *)anInvocation 开始转发处理 走到这一步,说明前面的操作都失败了。 运行时开始处理了:把消息相关的都封装成一个NSInvocation对象传递给当前这个方法。 ; 此方法提供一个方法签名对象描述被转发的消息,运行时根据这个对象生成转发消息的NSInvocation对象并传给forwardInvocation:。 :(NSInvocation *)anInvocation方法中的操作 这个方法里主要做两个操作: 1、找到能够响应 anInvocation对象的消息。
* invocation = [NSInvocation invocationWithMethodSignature:signature]; // [invocation * invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget * invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget * invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget * invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget
为唯一的警告标识,这里面的代码可以去除警告 [self performSelector:selector withObject:nil]; #pragma clang diagnostic pop 4 NSInvocation 的基本使用(任意调用对象消息方法并可以传N多参数的一个类) 在iOS中可以直接调用某个对象的消息方式有两种: performSelector:withObject; NSInvocation。 那么在这种情况下,我们就可以使用NSInvocation来进行这些相对复杂的操作。 signature = [ViewController methodSignatureForSelector:@selector(call)]; //2.通过MethodSignature来创建一个NSInvocation NSInvocation中保存了方法所属于的对象|方法名称|参数|返回值等等 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature
", nil]; [self performSelector:@selector(fooFirstInput:) withObject: array afterDelay:15.0]; 方法二: 使用NSInvocation NSInteger argument1 = 10; NSString *argument2 = @"argument2"; if([self respondsToSelector:aSelector]) { NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:aSelector]];
看这里,加代码具体来讲解:设计模式是什么鬼(观察者) Cocoa Touch框架内的实际应用 NSUndoManager是苹果提供的撤销恢复管理类,它里面主要有三个要素构成 恢复栈 撤销栈 命令对象:NSInvocation 图表显示如下: 进入撤销栈时,NSInvocation执行撤销命令 进入恢复栈时,NSInvocation执行恢复命令 开发中如何将它们合起来用呢? 在Command中添加命令执行对象NSInvocation 在Command中添加观察者列表,当命令执行是通知列表中的观察者 创建一个类似NSUndoManager的类,来管理整体commond列表
,将其基本用法总结如下: 一、初始化方法:有五种初始化方法,分别是 + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation invocation repeats:(BOOL)yesOrNo; - (void)viewDidLoad { [super viewDidLoad]; //初始化一个Invocation对象 NSInvocation * invo = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector 开始循环 [timer fire]; } + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation
若没有“备援的接受者”,则启动完成的消息转发机制,运行期系统会把与消息有关的细节全部封装到NSInvocation对象中,让接受者最后一次设法解决当前还未处理的这条消息 2.1动态方法解析 对象接收到无法解读的消息后 2.2.2 完整的消息转发 如果转发算法到了这一步,那么唯一能做的就是启用完整的消息转发机制,首先创建NSInvocation对象,把尚未处理的那条消息有关的全部细节都封于其中。 在触发NSInvocation对象时,“消息派发系统”将会把消息指派给目标对象。 此步骤会调用下列方法来转发消息: -(void)forwardInvocation:(NSInvocation *)invocation 该方法实现简单,只需要改变调用目标,使消息在新目标上得以调用即可 若想在第三部把消息转给备援接受者,还不如提前到第二步,因为第三步只是修改了调用目标,这项改动放在第二步执行更为简单,而且不用创建并处理完整的NSInvocation 四 总结 若对象无法响应某个选择子,
因为这一步不会创建任何新的对象,但下一步转发会创建一个 NSInvocation 对象,所以相对更快点。 Normal forwarding 这一步是 Runtime最后一次给你机会挽救程序。 如果是 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 返回了一个函数签名, Runtime 就会创建一个 NSInvocation 对象,并执行 - (void)forwardInvocation:(NSInvocation *)anInvocation 这个方法,转发消息给目标对象。 NSInvocation 实际上就是对一个消息的描述 ,包括方法名以及参数等信息。 所以我们可以在 - (void)forwardInvocation:(NSInvocation *)anInvocation 里修改传进来的 NSINvocation 对象, 然后发送 -invokeWithTarget
forwardInvocation: 在这个函数里可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。 (2) 这个时候runtime会将未知消息的所有细节都封装为NSInvocation对象,然后调用下述方法: - (void)forwardInvocation: (NSInvocation*)invocation ; 在这个函数里可以将NSInvocation多次转发到多个对象中。 了解更多 NSInvocation An NSInvocation is an Objective-C message rendered static, that is, it is an action NSInvocation有点类似于java里的反射,它有一套完整的装备:target,selector,returnValue,ArgumentArray,有了它们,NSInvocation就可以动态的
若没有“备援的接受者”,则启动完成的消息转发机制,运行期系统会把与消息有关的细节全部封装到NSInvocation对象中,让接受者最后一次设法解决当前还未处理的这条消息 2.1动态方法解析 对象接收到无法解读的消息后 2.2.2 完整的消息转发 如果转发算法到了这一步,那么唯一能做的就是启用完整的消息转发机制,首先创建NSInvocation对象,把尚未处理的那条消息有关的全部细节都封于其中。 在触发NSInvocation对象时,“消息派发系统”将会把消息指派给目标对象。 此步骤会调用下列方法来转发消息: -(void)forwardInvocation:(NSInvocation *)invocation 该方法实现简单,只需要改变调用目标,使消息在新目标上得以调用即可 若想在第三部把消息转给备援接受者,还不如提前到第二步,因为第三步只是修改了调用目标,这项改动放在第二步执行更为简单,而且不用创建并处理完整的NSInvocation 四 总结 若对象无法响应某个选择子,
如果此方法返回的是nil 或者self,则会进入消息转发机制(- (void)forwardInvocation:(NSInvocation *)invocation),否则将会向返回的对象重新发送消息 - (void)forwardInvocation:(NSInvocation *)invocation { SEL sel = invocation.selector; if([alternateObject 在forwardInvocation:消息发送前,runtime系统会向对象发送methodSignatureForSelector:消息,并取到返回的方法签名用于生成NSInvocation对象。 *viewControllerInvocation = [NSInvocation invocationWithMethodSignature:anInvocation.methodSignature 方案2的整体流程: 为即将转发的消息返回一个对应的方法签名(该签名后面用于对转发消息对象(NSInvocation *)anInvocation进行编码用) 开始消息转发((NSInvocation
; self.timer = nil; ---- + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo 参数: ti: 重复执行时间间隔 invocation: NSInvocation实例, 其用法见NSInvocation 的基本用法 yesOrNo: 是否重复执行 示例: // NSInvocation形式 - (void)timer2 { NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:)]; NSInvocation id)userInfo repeats:(BOOL)yesOrNo + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation
: invocation: repeats: + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation timerWithTimeInterval: invocation: repeats: + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation invocation:(NSInvocation *)invocation 启动 Timer – fire //加定时器 [[NSRunLoop currentRunLoop]run];
forwardingTargetForSelector:(SEL)aSelector,在这里你可以将消息转发给其他对象,如果实现则消息转发结束,否则执行步骤3 执行完整的消息转发机制,调用-(void)forwardInvocation:(NSInvocation 原理实例分析 BlocksKit 动态代理实现方式是最后一步,即-(void)forwardInvocation:(NSInvocation *)invocation,使得动态代理能够接受任意消息。 代理消息的转发由 A2DynamicDelegate 完成 - (void)forwardInvocation:(NSInvocation *)outerInv { SEL selector =