在 iOS中可以直接调用某个对象的消息方式有两种:
一种是performSelector:withObject;
再一种就是NSInvocation。
第一种方式比较简单,能完成简单的调用。但是对于>2个的参数或者有返回值的处理,那就需要做些额外工作才能搞定。那么在这种情况下,我们就可以使用NSInvocation来进行这些相对复杂的操作。
main.h
1 |
#import |
2 |
#import "MyClass.h" |
3 |
|
4 |
int
main
(int
argc, const
char
*
argv[]) |
5 |
{ |
6 |
|
7 |
NSAutoreleasePool
*
pool =
[[NSAutoreleasePool
alloc]
init]; |
8 |
|
9 |
MyClass
*myClass
=
[[MyClass
alloc]
init]; |
10 |
NSString
*myString
=
@"My
string"; |
11 |
|
12 |
//普通调用 |
13 |
NSString
*normalInvokeString
=
[myClass
appendMyString:myString]; |
14 |
NSLog(@"The
normal invoke string is: %@",
normalInvokeString); |
15 |
|
16 |
//NSInvocation调用 |
17 |
SEL
mySelector =
@selector(appendMyString:); |
18 |
NSMethodSignature
*
sig =
[[myClass
class] |
19 |
instanceMethodSignatureForSelector:
mySelector]; |
20 |
|
21 |
NSInvocation
*
myInvocation =
[NSInvocation
invocationWithMethodSignature:
sig]; |
22 |
[myInvocation
setTarget:
myClass]; |
23 |
[myInvocation
setSelector:
mySelector]; |
24 |
|
25 |
[myInvocation
setArgument:
&myString
atIndex:
2]; |
26 |
|
27 |
NSString
*
result =
nil; |
28 |
[myInvocation
retainArguments]; |
29 |
[myInvocation
invoke]; |
30 |
[myInvocation
getReturnValue:
&result]; |
31 |
NSLog(@"The
NSInvocation invoke string is: %@", result); |
32 |
|
33 |
[myClass
release]; |
34 |
|
35 |
[pool
drain]; |
36 |
return
0; |
37 |
} |
MyClass.h
1 |
#import |
2 |
|
3 |
|
4 |
@interface
MyClass
:
NSObject
{ |
5 |
} |
6 |
|
7 |
-
(NSString
*)appendMyString:(NSString
*)string; |
8 |
|
9 |
@end |
MyClass.m
1 |
#import "MyClass.h" |
2 |
|
3 |
|
4 |
@implementation
MyClass |
5 |
|
6 |
-
(id)init |
7 |
{ |
8 |
self
=
[super
init]; |
9 |
if
(self) { |
10 |
// Initialization code here. |
11 |
} |
12 |
|
13 |
return
self; |
14 |
} |
15 |
|
16 |
-
(NSString
*)appendMyString:(NSString
*)string |
17 |
{ |
18 |
NSString
*mString
=
[NSString
stringWithFormat:@"%@
after append method", string]; |
19 |
|
20 |
return
mString; |
21 |
} |
22 |
|
23 |
-
(void)dealloc |
24 |
{ |
25 |
[super
dealloc]; |
26 |
} |
27 |
|
28 |
@end |
这里说明一下[myInvocation setArgument: &myString atIndex:
2];为什么index从2开始 ,原因为:0 1 两个参数已经被target 和selector占用。
//方法签名类,需要被调用消息所属的类AsynInvoke
,被调用的消息invokeMethod:
NSMethodSignature *sig=[[AsynInvoke class] instanceMethodSignatureForSelector:@selector(invokeMethod:)];
//根据方法签名创建一个NSInvocation
NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:sig];
//设置调用者也就是AsynInvoked的实例对象,在这里我用self替代
[invocation setTarget:self];
//设置被调用的消息
[invocation setSelector:@selector(invokeMethod:)];
//如果此消息有参数需要传入,那么就需要按照如下方法进行参数设置,需要注意的是,atIndex的下标必须从2开始。原因为:0
1 两个参数已经被target和selector占用
NSInteger num=10;
[invocation setArgument:&num atIndex:2];
//retain 所有参数,防止参数被释放dealloc
[invocation retainArguments];
//消息调用
[invocation invoke];
//如果调用的消息有返回值,那么可进行以下处理
//获得返回值类型
const char *returnType
= sig.methodReturnType;
//声明返回值变量
id returnValue;
//如果没有返回值,也就是消息声明为void,那么returnValue=nil
if( !strcmp(returnType, @encode(void))
){
returnValue = nil;
}
//如果返回值为对象,那么为变量赋值
else if(
!strcmp(returnType, @encode(id))
){
[invocation getReturnValue:&returnValue];
}
else{
//如果返回值为普通类型NSInteger
BOOL
//返回值长度
NSUInteger length
= [sig methodReturnLength];
//根据长度申请内存
void *buffer =
(void *)malloc(length);
//为变量赋值
[invocation getReturnValue:buffer];
//以下代码为参考:具体地址我忘记了,等我找到后补上,(很对不起原作者)
if( !strcmp(returnType, @encode(BOOL))
) {
returnValue = [NSNumber numberWithBool:*((BOOL*)buffer)];
}
else if(
!strcmp(returnType, @encode(NSInteger))
){
returnValue = [NSNumber numberWithInteger:*((NSInteger*)buffer)];
}
returnValue = [NSValue valueWithBytes:buffer objCType:returnType];
}
到此为止,这个例子就可以正常的进行调用了