performSelector多个参数

来源:互联网 发布:c语言编程实战宝典pdf 编辑:程序博客网 时间:2024/06/06 00:58

需求如题所示。。。。。 我们都知道

- (id)performSelector:(SEL)aSelector;- (id)performSelector:(SEL)aSelector withObject:(id)object;- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

在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 : staticstatic 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

原创粉丝点击