参考:http://blog.csdn.net/zengconggen/article/details/38024625
在使用NStimer时需要在响应方法中传入多个参数,但是使用performSelector却只能传入两个参数,后来就看到方法中有一个是使用NSInvocation的,对这个类分析了解后发现正可满足要求。本篇文档可能有点浅显,基本的使用方式表述了出来,深层次的问题并未分析到,仅适合想初始了解其使用方式之友,后续如有深层次理解再来完善!
作用:NSInvocation
的作用和performSelector:withObject:
的作用是一样的:
—— 用于iOS 编程中调用某个对象的消息。
performSelector:withObject:
调用一些参数较少的消息是比较方便的,但是对于参数个数大于2的消息,使用NSInvocation
还是比较方便的。
因为NSInvocation
是静态的呈现Objective-C的消息,也就是说,它把一个行动变成了一个对象。NSInvocation
对象用于对象之间和应用程序之间存储和转发消息,主要通过NSTimer
对象和分布式对象系统来完成。
performSelector
与直接 调用和与NSInvocation
的区别
1 . 与直接调用的区别:
performSelector
是运行时系统负责去找方法的,在编译时不去做任何校验;如果直接调用编译器会自动检验。如果所用的那个方法不存在,那么直接调用在编译时就会发现(借助Xcode可以写完就发现),但如果使用performSelector
一定是在运行时才能发现(不过此时系统已经崩溃); Cocoa支持在运行时向某个类添加方法,即方法编译时不存在,但是运行时存在,这时候必然需要使用performSelector
去调用。所以为了程序的健壮性,会使用 respondsToSelector:(SEL)aSelector
; 来检测。
直接调用方法时,一定要在头文件中声明该方法的使用,也要将头文件import进来。而使用performSelector
时候,可以不用import头文件包含方法的对象,直接用performSelector
调用即可。
优点:运行时进行直接调用 ,不需要import头文件
2 . performSelector
与NSInvocation
的区别
performSelector
最多只能传入两个参数,如果使用NSInvocation
可以传入许多参数。
使用例子:
SEL method = @selector(fireTimer:andDate:);
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:method];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:method];
[invocation setArgument:&user atIndex:2];
[invocation setArgument:&date atIndex:3]; // 这里的两个参数就是对应着方法中的两个值来说的。
[invocation invoke];
如果使用了[NSTimer timerWithTimeInterval:2.0 invocation:invocation repeats:YES];
这样就相当于调用了invoke
还存在一个问题,如果这个方法有返回值,你要怎样处理
SEL selector = @selector(doSomeThingWithStr:andStr2:);
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:self]; // 第0个参数
[invocation setSelector:selector]; // 第1个参数
NSString *arg1 = @"testdemo";
NSString *arg2 = @"let's go";
[invocation setArgument:&arg1 atIndex:2];
[invocation setArgument:&arg2 atIndex:3];
[invocation invoke];
NSString *result;
[invocation getReturnValue:&result];
NSLog(@"result is %@",result);
在设置Enable Zombies
后发现,是由于系统多次释放NSArray * resultSet
造成的非法内存访问。
原因是在arc模式下,getReturnValue:
仅仅是从invocation
的返回值拷贝到指定的内存地址,如果返回值是一个NSObject
对象的话,是没有处理起内存管理的。而我们在定义resultSet
时使用的是__strong
类型的指针对象,arc
就会假设该内存块已被retain
(实际没有),当resultSet
出了定义域释放时,导致该crash。假如在定义之前有赋值的话,还会造成内存泄露的问题。
解决办法:
使用一个unretain的对象来获取返回值,或者 用void *
指针来保存返回值,然后用__bridge
来转化为OC对象。
// 方式一:
__unsafe_unretained NSString *result;
[invocation getReturnValue:&result];
NSLog(@"result is %@",result);
//方式二:
// void *result;
// [invocation getReturnValue:&result];
// NSLog(@"result is %@",(__bridge NSString *)result);
-(NSString *)doSomeThingWithStr:(NSString *)str1 andStr2:(NSString *)str2
{
NSLog(@"str1 is %@, str2 is %@", str1, str2);
return [str1 stringByAppendingString:str2];
}