关于直接调用方法和给对象发送消息调用方法(即perfromSelector和NSInvocation)
performSelector是运行时系统负责去找方法的,在编译时候不做任何校验;如果直接调用编译是会自动校验。如果imageDownloader:didFinishWithImage:image:不存在,那么直接调用 在编译时候就能够发现(借助Xcode可以写完就发现),但是使用performSelector的话一定是在运行时候才能发现(此时程序崩溃);Cocoa支持在运行时向某个类添加方法,即方法编译时不存在,但是运行时候存在,这时候必然需要使用performSelector去调用。所以有时候如果使用了performSelector,为了程序的健壮性,会使用检查方法
- (BOOL)respondsToSelector:(SEL)aSelector;
转自 http://blog.csdn.net/meegomeego/article/details/20041887
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