Created
October 27, 2017 03:44
-
-
Save yulingtianxia/1518fc7604ed65aa4ca98abdeee974e1 to your computer and use it in GitHub Desktop.
Convert NSInvocation and NSArray containing arguments. Under Developing!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NSString *MTExtractStructName(NSString *typeEncodeString) | |
{ | |
NSArray *array = [typeEncodeString componentsSeparatedByString:@"="]; | |
NSString *typeString = array[0]; | |
int firstValidIndex = 0; | |
for (int i = 0; i< typeString.length; i++) { | |
char c = [typeString characterAtIndex:i]; | |
if (c == '{' || c=='_') { | |
firstValidIndex++; | |
}else { | |
break; | |
} | |
} | |
return [typeString substringFromIndex:firstValidIndex]; | |
} | |
NSString *MTExtractTypeName(NSString *type) | |
{ | |
NSString *result = @""; | |
NSArray<NSString *> *components = [type componentsSeparatedByString:@""]; | |
for (int i = 0; i < components.count; i ++) { | |
if (i == 0) { | |
result = [result stringByAppendingString:components[i]]; | |
} | |
if (i > 0) { | |
result = [result stringByAppendingString:components[i].capitalizedString]; | |
} | |
} | |
return result; | |
} | |
BOOL MTIsTypeQualifier(char c) | |
{ | |
if (c == 'r' || c == 'n' || c == 'N' || c == 'o' || c == 'O' || c == 'R' || c == 'V') { | |
return YES; | |
} | |
return NO; | |
} | |
typedef NS_ENUM(NSUInteger, MTOperation) { | |
MTOperationLoadArgumentsToInvocation, | |
MTOperationStoreInvocationToArguments, | |
}; | |
void MTUpdateArgumentsInvocation(NSInvocation *invocation, NSMutableArray *args, MTOperation operation) | |
{ | |
NSUInteger numberOfArguments = invocation.methodSignature.numberOfArguments; | |
for (NSUInteger i = 2; i < numberOfArguments; i ++) { | |
const char *argumentType = [invocation.methodSignature getArgumentTypeAtIndex:i]; | |
switch (MTIsTypeQualifier(argumentType[0]) ? argumentType[1] : argumentType[0]) { | |
#define MT_FWD_ARG_CASE(_typeChar, _type, _typeCapitalizedString) \ | |
case _typeChar: { \ | |
_type arg; \ | |
if (MTOperationStoreInvocationToArguments == operation) { \ | |
[invocation getArgument:&arg atIndex:i]; \ | |
[args addObject:@(arg)]; \ | |
} \ | |
else if (MTOperationLoadArgumentsToInvocation == operation) { \ | |
NSNumber *number = args[i - 2]; \ | |
arg = [number _typeCapitalizedString##Value]; \ | |
[invocation setArgument:&arg atIndex:i]; \ | |
} \ | |
break; \ | |
} | |
MT_FWD_ARG_CASE('c', char, char) | |
MT_FWD_ARG_CASE('i', int, int) | |
MT_FWD_ARG_CASE('s', short, short) | |
MT_FWD_ARG_CASE('l', long, long) | |
MT_FWD_ARG_CASE('q', long long, longLong) | |
MT_FWD_ARG_CASE('C', unsigned char, unsignedChar) | |
MT_FWD_ARG_CASE('I', unsigned int, unsignedInt) | |
MT_FWD_ARG_CASE('S', unsigned short, unsignedShort) | |
MT_FWD_ARG_CASE('L', unsigned long, unsignedLong) | |
MT_FWD_ARG_CASE('Q', unsigned long long, unsignedLongLong) | |
MT_FWD_ARG_CASE('f', float, float) | |
MT_FWD_ARG_CASE('d', double, double) | |
MT_FWD_ARG_CASE('B', BOOL, bool) | |
case '*': { | |
const char *arg; | |
if (MTOperationStoreInvocationToArguments == operation) { | |
[invocation getArgument:&arg atIndex:i]; | |
NSValue *value = [NSValue valueWithBytes:&arg objCType:@encode(char **)]; | |
[args addObject:value]; | |
} | |
else if (MTOperationLoadArgumentsToInvocation == operation) { | |
arg = [args[i - 2] UTF8String]; | |
[invocation setArgument:&arg atIndex:2]; | |
} | |
break; | |
} | |
case '@': { | |
__unsafe_unretained id arg; | |
if (MTOperationStoreInvocationToArguments == operation) { | |
[invocation getArgument:&arg atIndex:i]; | |
if ([arg isKindOfClass:NSClassFromString(@"NSBlock")]) { | |
[args addObject:(arg ? [arg copy]: _nilObj)]; | |
} else { | |
[args addObject:(arg ? arg: _nilObj)]; | |
} | |
} | |
else if (MTOperationLoadArgumentsToInvocation == operation) { | |
arg = args[i - 2]; | |
// set nil as invocation args | |
[invocation setArgument:&arg atIndex:i]; | |
} | |
break; | |
} | |
case '#': { | |
Class arg; | |
if (MTOperationStoreInvocationToArguments == operation) { | |
[invocation getArgument:&arg atIndex:i]; | |
[args addObject:arg]; | |
} | |
else if (MTOperationLoadArgumentsToInvocation == operation) { | |
arg = args[i - 2]; | |
[invocation setArgument:&arg atIndex:i]; | |
} | |
break; | |
} | |
case ':': { | |
SEL selector; | |
if (MTOperationStoreInvocationToArguments == operation) { | |
[invocation getArgument:&selector atIndex:i]; | |
NSString *selectorName = NSStringFromSelector(selector); | |
[args addObject:(selectorName ? selectorName: _nilObj)]; | |
} | |
else if (MTOperationLoadArgumentsToInvocation == operation) { | |
NSString *selectorName = args[i - 2]; | |
if (_nilObj == selectorName) { | |
selector = NSSelectorFromString(@""); | |
} | |
else { | |
selector = NSSelectorFromString(selectorName); | |
} | |
[invocation setArgument:&selector atIndex:i]; | |
} | |
break; | |
} | |
#define MT_FWD_ARG_CTYPE \ | |
{ \ | |
void *arg; \ | |
if (MTOperationStoreInvocationToArguments == operation) { \ | |
[invocation getArgument:&arg atIndex:i]; \ | |
NSValue *value = [NSValue valueWithBytes:&arg objCType:argumentType]; \ | |
[args addObject:value]; \ | |
} \ | |
else if (MTOperationLoadArgumentsToInvocation == operation) { \ | |
NSValue *value = args[i - 2]; \ | |
if (@available(iOS 11, *)) { \ | |
[value getValue:&arg size:sizeof(argumentType)]; \ | |
} \ | |
else { \ | |
[value getValue:&arg]; \ | |
} \ | |
[invocation setArgument:&arg atIndex:i]; \ | |
} \ | |
break;\ | |
} | |
case '[': MT_FWD_ARG_CTYPE | |
case '{': { | |
NSString *structName = MTExtractStructName([NSString stringWithUTF8String:argumentType]); | |
#define MT_FWD_ARG_STRUCT(_type) \ | |
if ([structName rangeOfString:@#_type].location != NSNotFound) { \ | |
_type arg; \ | |
if (MTOperationStoreInvocationToArguments == operation) { \ | |
[invocation getArgument:&arg atIndex:i]; \ | |
NSValue *value = [NSValue valueWithBytes:&arg objCType:@encode(_type)]; \ | |
[args addObject:value]; \ | |
} \ | |
else if (MTOperationLoadArgumentsToInvocation == operation) { \ | |
NSValue *value = args[i - 2]; \ | |
if (@available(iOS 11, *)) { \ | |
[value getValue:&arg size:sizeof(_type)]; \ | |
} \ | |
else { \ | |
[value getValue:&arg]; \ | |
} \ | |
[invocation setArgument:&arg atIndex:i]; \ | |
} \ | |
break; \ | |
} | |
MT_FWD_ARG_STRUCT(CGPoint) | |
MT_FWD_ARG_STRUCT(CGVector) | |
MT_FWD_ARG_STRUCT(CGSize) | |
MT_FWD_ARG_STRUCT(CGRect) | |
MT_FWD_ARG_STRUCT(CGAffineTransform) | |
MT_FWD_ARG_STRUCT(UIEdgeInsets) | |
MT_FWD_ARG_STRUCT(NSDirectionalEdgeInsets) | |
MT_FWD_ARG_STRUCT(UIOffset) | |
MT_FWD_ARG_STRUCT(NSRange) | |
MT_FWD_ARG_CTYPE | |
break; | |
} | |
case '(': MT_FWD_ARG_CTYPE | |
case 'b': MT_FWD_ARG_CTYPE | |
case '^': { | |
void *arg; | |
if (MTOperationStoreInvocationToArguments == operation) { | |
[invocation getArgument:&arg atIndex:i]; | |
[args addObject:[NSValue value:arg withObjCType:argumentType]]; | |
} | |
else if (MTOperationLoadArgumentsToInvocation == operation) { | |
NSValue *value = args[i - 2]; | |
// TODO: get type pointer name | |
// if (@available(iOS 11, *)) { | |
// [value getValue:&arg size:sizeof()]; | |
// } | |
// else { | |
[value getValue:&arg]; | |
// } | |
[invocation setArgument:&arg atIndex:i]; | |
} | |
break; | |
} | |
case '?': | |
default: { | |
NSLog(@"error type %s", argumentType); | |
break; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, thanks for sharing and I have obtained a lot of help from you. I have a question, why
__unsafe_unretained
is necessary for object type? I encountered anEXC_BAD_ACCESS
while waiting for an expectation in unit test while__unsafe_unretained
is not presented, and I am very puzzled. Thanks again!