performSelector多个参数
performSelector多个参数
需求如题所示。。。。。 我们都知道
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
1
2
3
在NSObject中帮selector传递的参数个数最多是 2个 ,当时有时候如果某种需求。。。需要2个以上的参数该如何搞一搞了 ??? 其实网上有很多的例子
#import "NSObject+ArrayObjects.h" @implementation NSObject (ArrayObjects) -(id)performSelector:(SEL)aSelector withArray:(NSArray *)objects{ if (aSelector == nil || objects == nil) { @throw [NSException exceptionWithName:@"NullExcetption" reason: @"aSelector or objects is null" userInfo:nil]; return nil; } NSMethodSignature *methodSignature = [[self class] instanceMethodSignatureForSelector:aSelector]; if(methodSignature == nil) { @throw [NSException exceptionWithName:@"FunctionNotFoundExcetption" reason: [NSString stringWithFormat:@"the %@ not found",NSStringFromSelector(aSelector)] userInfo:nil]; return nil; } else { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [invocation setTarget:self]; [invocation setSelector:aSelector]; //签名中方法参数的个数,内部包含了self和_cmd,所以参数从第3个开始 NSInteger signatureParamCount = methodSignature.numberOfArguments - 2; NSInteger requireParamCount = objects.count; NSInteger resultParamCount = MIN(signatureParamCount, requireParamCount); for (NSInteger i = 0; i < resultParamCount; i++) { id obj = objects[i]; [invocation setArgument:&obj atIndex:i+2]; } [invocation invoke]; id callBackObject = nil; if(methodSignature.methodReturnLength) { [invocation getReturnValue:&callBackObject]; } return callBackObject; } } @end
虽然这个一度娘就能度到,而且感觉很牛逼,其实是有bug和缺陷性的,然后对数组里面的数据有要求,我们都知道OC是兼容C代码的。。往里面放入block或结构体就瞎了。。。。。。 我在测试AFNetworking项目的时候发现上面代码存在问题,然后找到了一个比较好一点的解决方案 ,当然github上搞一搞的 ,感觉还可以没报错
#import <Foundation/Foundation.h> @interface NSObject (VKMsgSend) + (id)VKCallSelector:(SEL)selector error:(NSError *__autoreleasing *)error,...; + (id)VKCallSelectorName:(NSString *)selName error:(NSError *__autoreleasing *)error,...; - (id)VKCallSelector:(SEL)selector error:(NSError *__autoreleasing *)error,...; - (id)VKCallSelectorName:(NSString *)selName error:(NSError *__autoreleasing *)error,...; @end @interface NSString (VKMsgSend) - (id)VKCallClassSelector:(SEL)selector error:(NSError *__autoreleasing *)error,...; - (id)VKCallClassSelectorName:(NSString *)selName error:(NSError *__autoreleasing *)error,...; - (id)VKCallClassAllocInitSelector:(SEL)selector error:(NSError *__autoreleasing *)error,...; - (id)VKCallClassAllocInitSelectorName:(NSString *)selName error:(NSError *__autoreleasing *)error,...; @end
#import "VKMsgSend.h" #if TARGET_OS_IPHONE #import <UIKit/UIApplication.h> #endif #pragma mark : vk_nilObject @interface vk_pointer : NSObject @property (nonatomic) void *pointer; @end @implementation vk_pointer @end @interface vk_nilObject : NSObject @end @implementation vk_nilObject @end #pragma mark : static static NSLock *_vkMethodSignatureLock; static NSMutableDictionary *_vkMethodSignatureCache; static vk_nilObject *vknilPointer = nil; static NSString *vk_extractStructName(NSString *typeEncodeString){ NSArray *array = [typeEncodeString componentsSeparatedByString:@"="]; NSString *typeString = array[0]; __block int firstVaildIndex = 0; [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { unichar c = [typeEncodeString characterAtIndex:idx]; if (c=='{'||c=='_') { firstVaildIndex++; }else{ *stop = YES; } }]; return [typeString substringFromIndex:firstVaildIndex]; } static NSString *vk_selectorName(SEL selector){ const char *selNameCstr = sel_getName(selector); NSString *selName = [[NSString alloc]initWithUTF8String:selNameCstr]; return selName; } static NSMethodSignature *vk_getMethodSignature(Class cls, SEL selector){ if (!_vkMethodSignatureLock) { _vkMethodSignatureLock = [[NSLock alloc] init]; } [_vkMethodSignatureLock lock]; if (!_vkMethodSignatureCache) { _vkMethodSignatureCache = [[NSMutableDictionary alloc]init]; } if (!_vkMethodSignatureCache[cls]) { _vkMethodSignatureCache[(id<NSCopying>)cls] =[[NSMutableDictionary alloc]init]; } NSString *selName = vk_selectorName(selector); NSMethodSignature *methodSignature = _vkMethodSignatureCache[cls][selName]; if (!methodSignature) { methodSignature = [cls instanceMethodSignatureForSelector:selector]; if (methodSignature) { _vkMethodSignatureCache[cls][selName] = methodSignature; }else { methodSignature = [cls methodSignatureForSelector:selector]; if (methodSignature) { _vkMethodSignatureCache[cls][selName] = methodSignature; } } } [_vkMethodSignatureLock unlock]; return methodSignature; } static void vk_generateError(NSString *errorInfo, NSError **error){ if (error) { *error = [NSError errorWithDomain:errorInfo code:0 userInfo:nil]; } } static id vk_targetCallSelectorWithArgumentError(id target, SEL selector, NSArray *argsArr, NSError *__autoreleasing *error){ Class cls = [target class]; NSMethodSignature *methodSignature = vk_getMethodSignature(cls, selector); NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [invocation setTarget:target]; [invocation setSelector:selector]; NSMutableArray* _markArray; for (NSUInteger i = 2; i< [methodSignature numberOfArguments]; i++) { const char *argumentType = [methodSignature getArgumentTypeAtIndex:i]; id valObj = argsArr[i-2]; switch (argumentType[0]=='r'?argumentType[1]:argumentType[0]) { #define VK_CALL_ARG_CASE(_typeString, _type, _selector) \ case _typeString: { \ _type value = [valObj _selector]; \ [invocation setArgument:&value atIndex:i];\ break; \ } VK_CALL_ARG_CASE('c', char, charValue) VK_CALL_ARG_CASE('C', unsigned char, unsignedCharValue) VK_CALL_ARG_CASE('s', short, shortValue) VK_CALL_ARG_CASE('S', unsigned short, unsignedShortValue) VK_CALL_ARG_CASE('i', int, intValue) VK_CALL_ARG_CASE('I', unsigned int, unsignedIntValue) VK_CALL_ARG_CASE('l', long, longValue) VK_CALL_ARG_CASE('L', unsigned long, unsignedLongValue) VK_CALL_ARG_CASE('q', long long, longLongValue) VK_CALL_ARG_CASE('Q', unsigned long long, unsignedLongLongValue) VK_CALL_ARG_CASE('f', float, floatValue) VK_CALL_ARG_CASE('d', double, doubleValue) VK_CALL_ARG_CASE('B', BOOL, boolValue) case ':':{ NSString *selName = valObj; SEL selValue = NSSelectorFromString(selName); [invocation setArgument:&selValue atIndex:i]; } break; case '{':{ NSString *typeString = vk_extractStructName([NSString stringWithUTF8String:argumentType]); NSValue *val = (NSValue *)valObj; #define vk_CALL_ARG_STRUCT(_type, _methodName) \ if ([typeString rangeOfString:@#_type].location != NSNotFound) { \ _type value = [val _methodName]; \ [invocation setArgument:&value atIndex:i]; \ break; \ } vk_CALL_ARG_STRUCT(CGRect, CGRectValue) vk_CALL_ARG_STRUCT(CGPoint, CGPointValue) vk_CALL_ARG_STRUCT(CGSize, CGSizeValue) vk_CALL_ARG_STRUCT(NSRange, rangeValue) vk_CALL_ARG_STRUCT(CGAffineTransform, CGAffineTransformValue) vk_CALL_ARG_STRUCT(UIEdgeInsets, UIEdgeInsetsValue) vk_CALL_ARG_STRUCT(UIOffset, UIOffsetValue) vk_CALL_ARG_STRUCT(CGVector, CGVectorValue) } break; case '*':{ NSCAssert(NO, @"argument boxing wrong,char* is not supported"); } break; case '^':{ vk_pointer *value = valObj; void *pointer = value.pointer; id obj = *((__unsafe_unretained id *)pointer); if (!obj) { if (argumentType[1] == '@') { if (!_markArray) { _markArray = [[NSMutableArray alloc] init]; } [_markArray addObject:valObj]; } } [invocation setArgument:&pointer atIndex:i]; } break; case '#':{ [invocation setArgument:&valObj atIndex:i]; } break; default:{ if ([valObj isKindOfClass:[vk_nilObject class]]) { [invocation setArgument:&vknilPointer atIndex:i]; }else{ [invocation setArgument:&valObj atIndex:i]; } } } } [invocation invoke]; if ([_markArray count] > 0) { for (vk_pointer *pointerObj in _markArray) { void *pointer = pointerObj.pointer; id obj = *((__unsafe_unretained id *)pointer); if (obj) { CFRetain((__bridge CFTypeRef)(obj)); } } } const char *returnType = [methodSignature methodReturnType]; NSString *selName = vk_selectorName(selector); if (strncmp(returnType, "v", 1) != 0 ) { if (strncmp(returnType, "@", 1) == 0) { void *result; [invocation getReturnValue:&result]; if (result == NULL) { return nil; } id returnValue; if ([selName isEqualToString:@"alloc"] || [selName isEqualToString:@"new"] || [selName isEqualToString:@"copy"] || [selName isEqualToString:@"mutableCopy"]) { returnValue = (__bridge_transfer id)result; }else{ returnValue = (__bridge id)result; } return returnValue; } else { switch (returnType[0] == 'r' ? returnType[1] : returnType[0]) { #define vk_CALL_RET_CASE(_typeString, _type) \ case _typeString: { \ _type returnValue; \ [invocation getReturnValue:&returnValue];\ return @(returnValue); \ break; \ } vk_CALL_RET_CASE('c', char) vk_CALL_RET_CASE('C', unsigned char) vk_CALL_RET_CASE('s', short) vk_CALL_RET_CASE('S', unsigned short) vk_CALL_RET_CASE('i', int) vk_CALL_RET_CASE('I', unsigned int) vk_CALL_RET_CASE('l', long) vk_CALL_RET_CASE('L', unsigned long) vk_CALL_RET_CASE('q', long long) vk_CALL_RET_CASE('Q', unsigned long long) vk_CALL_RET_CASE('f', float) vk_CALL_RET_CASE('d', double) vk_CALL_RET_CASE('B', BOOL) case '{': { NSString *typeString = vk_extractStructName([NSString stringWithUTF8String:returnType]); #define vk_CALL_RET_STRUCT(_type) \ if ([typeString rangeOfString:@#_type].location != NSNotFound) { \ _type result; \ [invocation getReturnValue:&result];\ NSValue * returnValue = [NSValue valueWithBytes:&(result) objCType:@encode(_type)];\ return returnValue;\ } vk_CALL_RET_STRUCT(CGRect) vk_CALL_RET_STRUCT(CGPoint) vk_CALL_RET_STRUCT(CGSize) vk_CALL_RET_STRUCT(NSRange) vk_CALL_RET_STRUCT(CGAffineTransform) vk_CALL_RET_STRUCT(UIEdgeInsets) vk_CALL_RET_STRUCT(UIOffset) vk_CALL_RET_STRUCT(CGVector) } break; case '*':{ } break; case '^': { } break; case '#': { } break; } return nil; } } return nil; }; static NSArray *vk_targetBoxingArguments(va_list argList, Class cls, SEL selector, NSError *__autoreleasing *error){ NSMethodSignature *methodSignature = vk_getMethodSignature(cls, selector); NSString *selName = vk_selectorName(selector); if (!methodSignature) { NSString* errorStr = [NSString stringWithFormat:@"unrecognized selector (%@)", selName]; vk_generateError(errorStr,error); return nil; } NSMutableArray *argumentsBoxingArray = [[NSMutableArray alloc]init]; for (NSUInteger i = 2; i < [methodSignature numberOfArguments]; i++) { const char *argumentType = [methodSignature getArgumentTypeAtIndex:i]; switch (argumentType[0] == 'r' ? argumentType[1] : argumentType[0]) { #define vk_BOXING_ARG_CASE(_typeString, _type)\ case _typeString: {\ _type value = va_arg(argList, _type);\ [argumentsBoxingArray addObject:@(value)];\ break; \ }\ vk_BOXING_ARG_CASE('c', int) vk_BOXING_ARG_CASE('C', int) vk_BOXING_ARG_CASE('s', int) vk_BOXING_ARG_CASE('S', int) vk_BOXING_ARG_CASE('i', int) vk_BOXING_ARG_CASE('I', unsigned int) vk_BOXING_ARG_CASE('l', long) vk_BOXING_ARG_CASE('L', unsigned long) vk_BOXING_ARG_CASE('q', long long) vk_BOXING_ARG_CASE('Q', unsigned long long) vk_BOXING_ARG_CASE('f', double) vk_BOXING_ARG_CASE('d', double) vk_BOXING_ARG_CASE('B', int) case ':': { SEL value = va_arg(argList, SEL); NSString *selValueName = NSStringFromSelector(value); [argumentsBoxingArray addObject:selValueName]; } break; case '{': { NSString *typeString = vk_extractStructName([NSString stringWithUTF8String:argumentType]); #define vk_FWD_ARG_STRUCT(_type, _methodName) \ if ([typeString rangeOfString:@#_type].location != NSNotFound) { \ _type val = va_arg(argList, _type);\ NSValue* value = [NSValue _methodName:val];\ [argumentsBoxingArray addObject:value]; \ break; \ } vk_FWD_ARG_STRUCT(CGRect, valueWithCGRect) vk_FWD_ARG_STRUCT(CGPoint, valueWithCGPoint) vk_FWD_ARG_STRUCT(CGSize, valueWithCGSize) vk_FWD_ARG_STRUCT(NSRange, valueWithRange) vk_FWD_ARG_STRUCT(CGAffineTransform, valueWithCGAffineTransform) vk_FWD_ARG_STRUCT(UIEdgeInsets, valueWithUIEdgeInsets) vk_FWD_ARG_STRUCT(UIOffset, valueWithUIOffset) vk_FWD_ARG_STRUCT(CGVector, valueWithCGVector) } break; case '*':{ vk_generateError(@"unsupported char* argumenst",error); return nil; } break; case '^': { void *value = va_arg(argList, void**); vk_pointer *pointerObj = [[vk_pointer alloc]init]; pointerObj.pointer = value; [argumentsBoxingArray addObject:pointerObj]; } break; case '#': { Class value = va_arg(argList, Class); [argumentsBoxingArray addObject:(id)value]; // vk_generateError(@"unsupported class argumenst",error); // return nil; } break; case '@':{ id value = va_arg(argList, id); if (value) { [argumentsBoxingArray addObject:value]; }else{ [argumentsBoxingArray addObject:[vk_nilObject new]]; } } break; default: { vk_generateError(@"unsupported argumenst",error); return nil; } } } return [argumentsBoxingArray copy]; } @implementation NSObject (VKMsgSend) + (id)VKCallSelectorName:(NSString *)selName error:(NSError *__autoreleasing *)error,...{ va_list argList; va_start(argList, error); SEL selector = NSSelectorFromString(selName); NSArray *boxingAruments = vk_targetBoxingArguments(argList, [self class], selector, error); va_end(argList); if (!boxingAruments) { return nil; } return vk_targetCallSelectorWithArgumentError(self, selector, boxingAruments, error); } + (id)VKCallSelector:(SEL)selector error:(NSError *__autoreleasing *)error,...{ va_list argList; va_start(argList, error); NSArray* boxingArguments = vk_targetBoxingArguments(argList, [self class], selector, error); va_end(argList); if (!boxingArguments) { return nil; } return vk_targetCallSelectorWithArgumentError(self, selector, boxingArguments, error); } - (id)VKCallSelectorName:(NSString *)selName error:(NSError *__autoreleasing *)error,...{ va_list argList; va_start(argList, error); SEL selector = NSSelectorFromString(selName); NSArray* boxingArguments = vk_targetBoxingArguments(argList, [self class], selector, error); va_end(argList); if (!boxingArguments) { return nil; } return vk_targetCallSelectorWithArgumentError(self, selector, boxingArguments, error); } - (id)VKCallSelector:(SEL)selector error:(NSError *__autoreleasing *)error,...{ va_list argList; va_start(argList, error); NSArray* boxingArguments = vk_targetBoxingArguments(argList, [self class], selector, error); va_end(argList); if (!boxingArguments) { return nil; } return vk_targetCallSelectorWithArgumentError(self, selector, boxingArguments, error); } @end @implementation NSString (VKMsgSend) -(id)VKCallClassSelector:(SEL)selector error:(NSError *__autoreleasing *)error, ... { Class cls = NSClassFromString(self); if (!cls) { NSString* errorStr = [NSString stringWithFormat:@"unrecognized className (%@)", self]; vk_generateError(errorStr,error); return nil; } va_list argList; va_start(argList, error); NSArray* boxingArguments = vk_targetBoxingArguments(argList, cls, selector, error); va_end(argList); if (!boxingArguments) { return nil; } return vk_targetCallSelectorWithArgumentError(cls, selector, boxingArguments, error); } -(id)VKCallClassSelectorName:(NSString *)selName error:(NSError *__autoreleasing *)error, ... { Class cls = NSClassFromString(self); if (!cls) { NSString* errorStr = [NSString stringWithFormat:@"unrecognized className (%@)", self]; vk_generateError(errorStr,error); return nil; } SEL selector = NSSelectorFromString(selName); va_list argList; va_start(argList, error); NSArray* boxingArguments = vk_targetBoxingArguments(argList, cls, selector, error); va_end(argList); if (!boxingArguments) { return nil; } return vk_targetCallSelectorWithArgumentError(cls, selector, boxingArguments, error); } -(id)VKCallClassAllocInitSelector:(SEL)selector error:(NSError *__autoreleasing *)error, ... { Class cls = NSClassFromString(self); if (!cls) { NSString* errorStr = [NSString stringWithFormat:@"unrecognized className (%@)", self]; vk_generateError(errorStr,error); return nil; } va_list argList; va_start(argList, error); NSArray* boxingArguments = vk_targetBoxingArguments(argList, cls, selector, error); va_end(argList); if (!boxingArguments) { return nil; } id allocObj = [cls alloc]; return vk_targetCallSelectorWithArgumentError(allocObj, selector, boxingArguments, error); } -(id)VKCallClassAllocInitSelectorName:(NSString *)selName error:(NSError *__autoreleasing *)error, ... { Class cls = NSClassFromString(self); if (!cls) { NSString* errorStr = [NSString stringWithFormat:@"unrecognized className (%@)", self]; vk_generateError(errorStr,error); return nil; } SEL selector = NSSelectorFromString(selName); va_list argList; va_start(argList, error); NSArray* boxingArguments = vk_targetBoxingArguments(argList, cls, selector, error); va_end(argList); if (!boxingArguments) { return nil; } id allocObj = [cls alloc]; return vk_targetCallSelectorWithArgumentError(allocObj, selector, boxingArguments, error); } @end
至于怎么使用 。。。。。。。。。
#import "ViewController.h" #import "VKMsgSend.h" #import <objc/message.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; Class cls = NSClassFromString(@"testClassA"); id abc = [[cls alloc]init]; NSError *err; //this warning is ok Selector is in testClassA NSString *return1 = [abc VKCallSelector:@selector(testfunction:withB:) error:&err,4,3.5f]; NSLog(@"%@",return1); NSNumber *return3 = [abc VKCallSelectorName:@"testfunction:withB:withC:" error:nil,4,3.5,@"haha"]; NSInteger tureReturn3 = [return3 integerValue]; NSLog(@"%@",@(tureReturn3)); NSString *return4 = [abc VKCallSelectorName:@"testfunction:withB:withC:withD:" error:nil,4,3.5,nil, CGRectMake(10, 10, 10, 10)]; NSLog(@"%@",return4); NSError* testerr2; [abc VKCallSelectorName:@"testFunctionError:" error:nil,&testerr2]; NSLog(@"see more test case in XCTest Target"); NSLog(@"vk_msgSend_projTests"); //这是一段展示 performselector 缺点和不足的代码,有注释中文解释 [self performShow]; //这是一段展示 objc_msgsend 缺点和不足的代码,有注释和中文解释 [self msgsendShow]; //这是对比展示 VKMsgSend的代码 [self vkshow]; } -(void)performShow { Class cls = NSClassFromString(@"testClassA"); id abc = [[cls alloc]init]; //-(NSString*)testfunction:(int)num withB:(float)boolv NSString * result = [abc performSelector:@selector(testfunction:withB:) withObject:@4 withObject:@3.5]; NSLog(@"%@",result); //并且只支持id,如果你敢把基础数值类型封装成number传进去,数值还是错乱的 //这样代码跑进去 int 传了个NSNumber进去 函数内指针全乱,参数值都飞了 //3个参数就不支持了,打开注释你会发现,就没有传3个参数的方法 // [abc performSelector:@selector(testfunction:withB:withC:) withObject:@4.5 withObject:@3 withObject:@"ssss"]; } -(void)msgsendShow { Class cls = NSClassFromString(@"testClassA"); id abc = [[cls alloc]init]; // NSString *result = objc_msgSend(abc, @selector(testfunction:withB:), 4, 3.5); //很抱歉上面这样的方法,看着用的很方便,但是在iOS 64位下会直接崩溃,xcode8下是直接无法编译 NSString *result2 = ((NSString* (*)(id, SEL, int,float))objc_msgSend)(abc, @selector(testfunction:withB:), 4, 3.5); //看到没必须这么费劲的写一坨C语言的函数指针强转才可以 NSLog(@"%@",result2); } -(void)vkshow { //理想状态下 旧的 objc_msgSend就已经很方便了,但是已经不能这么用了,那我就封装出了一个runtime工具 Class cls = NSClassFromString(@"testClassA"); id abc = [[cls alloc]init]; NSError * error; //很方便吧 [abc VKCallSelectorName:@"testfunction:withB:" error:&error,4,3.5]; //支持所有基础类型,结构体,id类型,class类型,selector类型,block类型,还有指针类型 ** //如果是使用类方法,还可以直接通过类名NSString [@"testClassA" VKCallClassSelectorName:@"testfunction:withB:withH" error:&error,4,3.5,@"aaa"]; //如果是实例方法,可以直接通过类名NSString,调用init selector,哪怕initWithXX:XX:等自定义的初始化函数都可以 id abcc = [@"testClassA" VKCallClassAllocInitSelectorName:@"init" error:nil]; NSLog(@"%@",abcc); //省去了手写NSClassFromString 的事情 }
当然,这里附上github地址 https://github.com/Awhisper/VKMsgSend
————————————————
版权声明:本文为CSDN博主「hebia0」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chmod_R_755/article/details/78676395