关于直接调用方法和给对象发送消息调用方法(即perfromSelector和NSInvocation)
performSelector是运行时系统负责去找方法的,在编译时候不做任何校验;如果直接调用编译是会自动校验。如果imageDownloader:didFinishWithImage:image:不存在,那么直接调用 在编译时候就能够发现(借助Xcode可以写完就发现),但是使用performSelector的话一定是在运行时候才能发现(此时程序崩溃);Cocoa支持在运行时向某个类添加方法,即方法编译时不存在,但是运行时候存在,这时候必然需要使用performSelector去调用。所以有时候如果使用了performSelector,为了程序的健壮性,会使用检查方法
- (BOOL)respondsToSelector:(SEL)aSelector;
转自
1、perfromSelector
2、NSInvocation
NSInvocation 比起 perfromSelector 的好处,可以传递不止两个参数
1)NSInvocation使用方式一
// 1、创建“方法调用”对象:NSInvocation(invocation 的英文含义:调用)
// 1>、设置“方法签名”对象,到底怎么理解这个方法签名呢???
NSMethodSignature *signature = [ViewController instanceMethodSignatureForSelector:@selector(sendMessageWithNumber:andContent:status:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
// 2>、要执行谁里面的---
invocation.target = self;
// 3>、什么方法
invocation.selector = @selector(sendMessageWithNumber:andContent:status:);
// 4>、传递的参数
// 注意:自定义的参数索引从2开始, 0是self / 1是_cmd;
// 方法签名中保存的方法名称, 必须和invocation的selector中保存的方法名称一样;
NSString *number = @"10086";
NSString *content = @"你好";
NSString *status = @"发送成功";
[invocation setArgument:&number atIndex:2];
[invocation setArgument:&content atIndex:3];
[invocation setArgument:&status atIndex:4];
// 2、执行调用
[invocation invoke];
2)NSInvocation方式二:给NSObject添加一个分类
.h文件
#import <Foundation/Foundation.h>
@interface NSObject (CY)
- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects;
@end
.m文件
#import "NSObject+CY.h"
@implementation NSObject (CY)
- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects
{
// 签名中保存了方法的名称/参数/返回值类型
// 注意: 签名一般是用来设置参数和获取返回值的, 和方法的调用没有太大的关系
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
// 0.判断传入的方法是否存在, 如果不存在就进行相应处理
if (signature == nil) {
// 如果方法不存在, 就抛出一个异常
// @throw [NSException exceptionWithName:@"小码哥课堂特定异常" reason:@"你太SB了" userInfo:nil];
NSString *info = [NSString stringWithFormat:@"%@方法找不到", NSStringFromSelector(aSelector)];
[NSException raise:@"特定异常" format:info, nil];
// return nil;
}
// 1.创建一个NSInvocation对象
// NSInvocation中保存了方法所属的对象/方法名称/参数/返回值
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = aSelector;
// 注意: 如果需要通过遍历给invocation设置参数, 那么不能遍历objects数组
// 因为objects数组的长度是不可控
// 注意: 通过numberOfArguments方法获取的参数个数, 是包含self和_cmd的
NSUInteger argsCount = signature.numberOfArguments - 2; // 3
NSUInteger arrCount = objects.count; // 4 2
NSUInteger count = MIN(argsCount, arrCount);
// for (int i = 0; i < objects.count; i++) {
for (int i = 0; i < count; i++) {
id obj = objects[i];
// 判断需要设置的参数是否是NSNull, 如果是就设置为nil
if ([obj isKindOfClass:[NSNull class]]) {
obj = nil;
}
[invocation setArgument:&obj atIndex:i + 2];
}
// 2.调用NSInvocation对象的invoke方法
[invocation invoke];
// 3.获取方法的返回值
id res = nil;
// 3.1判断当前调用的方法, 是否有返回值
// NSLog(@"methodReturnType = %s", signature.methodReturnType);
// NSLog(@"methodReturnLength = %zd", signature.methodReturnLength);
if (signature.methodReturnLength != 0) {
[invocation getReturnValue:&res];
}
return res;
}
@end