Skip to content

Instantly share code, notes, and snippets.

@yulingtianxia
Created October 27, 2017 03:44
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yulingtianxia/1518fc7604ed65aa4ca98abdeee974e1 to your computer and use it in GitHub Desktop.
Save yulingtianxia/1518fc7604ed65aa4ca98abdeee974e1 to your computer and use it in GitHub Desktop.
Convert NSInvocation and NSArray containing arguments. Under Developing!
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;
}
}
}
}
@54lihaoxin
Copy link

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 an EXC_BAD_ACCESS while waiting for an expectation in unit test while __unsafe_unretained is not presented, and I am very puzzled. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment