API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| + (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig;
@property (readonly, retain) NSMethodSignature *methodSignature;
- (void)retainArguments;
@property (readonly) BOOL argumentsRetained;
@property (nullable, assign) id target;
@property 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;
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| SEL sel = NSSelectorFromString(@"method");
NSMethodSignature *sig = [target methodSignatureForSelector:sel];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:target];
[invocation setSelector:sel];
int arg = 1; [invocation setArgument:&arg atIndex:2];
[invocation invoke];
|
注意事项
setArgument
objc_msgSend
是一个_参数个数可变的函数_,第一个参数代表接受者,第二个参数代表选择子,后续参数就是消息中的那些参数
1
| void objc_msgSend(id self, SEL cmd, ...);
|
invoke
就是调用objc_msgSend
发送消息,setArgument:atIndex:
是设置消息参数,所以
1 2 3 4 5 6
| [invocation setTarget:target]; [invocation setSelector:sel];
[invocation setArgument:&target atIndex:0]; [invocation setArgument:&selector atIndex:1];
|
当传入其它参数时,atIndex
一定是从2
开始
getReturnValue
当没有返回值时,不可以使用getReturnValue
1 2 3
| id returnValue = nil; [invocation getReturnValue:&returnValue];
|
以上代码在大多数情况下会 Crash,返回值可分为两种类型
返回值是对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| - (NSDictionary *)hello { ... return @{@"key": @"value"}; }
SEL sel = NSSelectorFromString(@"hello"); NSMethodSignature *sig = [target methodSignatureForSelector:sel]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:target]; [invocation setSelector:sel]; [invocation invoke];
id returnValue = nil; void *tempResult = nil; [invocation getReturnValue:&tempResult]; returnValue = (__bridge id)tempResult;
__autoreleasing id returnValue = nil; [invocation getReturnValue:&returnValue];
__unsafe_unretained id returnValue = nil; [invocation getReturnValue:&returnValue];
|
返回值是基本类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| - (NSInteger)hello { ... return 1; }
SEL sel = NSSelectorFromString(@"hello"); NSMethodSignature *sig = [target methodSignatureForSelector:sel]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:target]; [invocation setSelector:sel]; [invocation invoke];
id returnValue = nil; NSUInteger length = [sig methodReturnLength]; void *buffer = (void *)malloc(length); [invocation getReturnValue:buffer];
returnValue = [NSNumber numberWithInteger:*((NSInteger *)buffer)];
returnValue = [NSNumber numberWithBool:*((BOOL *)buffer)];
returnValue = [NSValue valueWithBytes:buffer objCType:returnType];
|
不产生 Crash 情况
列举一种,应该还有其它情况
1 2 3 4
| - (NSString *)hello { ... return @"hello"; }
|
编译器在编译时应该做了某种优化,具体不清楚
NSMethodSignature
通过 methodReturnLength
methodReturnType
判断有无返回值及返回值类型
API
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| + (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;
@property (readonly) NSUInteger numberOfArguments;
- (const char *)getArgumentTypeAtIndex:(NSUInteger)idx NS_RETURNS_INNER_POINTER;
@property (readonly) NSUInteger frameLength;
- (BOOL)isOneway;
@property (readonly) const char *methodReturnType NS_RETURNS_INNER_POINTER;
@property (readonly) NSUInteger methodReturnLength;
|
判断返回值类型
1 2 3 4 5 6 7 8
| const char *returnType = sig.methodReturnType; NSUInteger returnLength = sig.methodReturnLength;
if (!returnLength) {...}
if (!strcmp(returnType, @encode(id))) {...}
if (!strcmp(returnType, @encode(BOOL))) {...}
|