[button addTarget:self action:[self ax_lambda:^(UIButton *sender, UIEvent *event){ NSLog(@"click on button %@, event = %@", sender, event); }] forControlEvents:UIControlEventTouchUpInside]; [button addTarget:self action:[self ax_lambda:^{ NSLog(@"click"); }] forControlEvents:UIControlEventTouchUpInside];
__block NSInteger sum = 0; [self performSelector:[self ax_lambda:^(NSNumber *argA, NSNumber *argB) { sum = [argA integerValue] + [argB integerValue]; }] withObject:@(2) withObject:@(3)]; //sum — 5 SEL selSum = [self ax_lambda:^NSInteger(NSInteger argA, NSInteger argB){ return argA + argB; }]; NSInteger(*funcSum)(id, SEL, NSInteger, NSInteger) = (NSInteger(*)(id, SEL, NSInteger, NSInteger))objc_msgSend; NSInteger sum2 = funcSum(self, selSum, 2, 3); //sum2 — 5
typedef void(^AXProxyBlockInterpose)(NSInvocation *invocation); @interface AXProxyBlock : NSProxy + (instancetype)initWithBlock:(id)block; - (void)setBeforeInvoke:(AXProxyBlockInterpose)beforeInvoke; - (NSString *)blockSignatureStringCTypes; @end
typedef struct AXBlockStruct_1 { unsigned long int reserved; unsigned long int size; void (*copy_helper)(void *dst, void *src); void (*dispose_helper)(void *src); const char *signature; } AXBlockStruct_1; typedef struct AXBlockStruct { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct AXBlockStruct_1 *descriptor; } AXBlockStruct; typedef NS_ENUM(NSUInteger, AXBlockFlag) { AXBlockFlag_HasCopyDispose = (1 << 25), AXBlockFlag_HasCtor = (1 << 26), AXBlockFlag_IsGlobal = (1 << 28), AXBlockFlag_HasStret = (1 << 29), AXBlockFlag_HasSignature = (1 << 30) };
@interface AXProxyBlock () { // isa NSProxy, int _flags; int _reserved; IMP _invoke; AXBlockStruct_1 *_descriptor; // , , AXProxyBlockInterpose _beforeInvoke; id _block; NSMethodSignature *_blockMethodSignature; IMP _impBlockInvoke; } @end
- (instancetype)initWithBlock:(id)block { if (self != nil) { AXBlockStruct *blockRef = (__bridge AXBlockStruct *)block; _flags = blockRef->flags; _reserved = blockRef->reserved; _descriptor = calloc(1, sizeof(AXBlockStruct_1)); _descriptor->size = class_getInstanceSize([self class]); BOOL flag_stret = _flags & AXBlockFlag_HasStret; _invoke = (flag_stret ? (IMP)_objc_msgForward_stret : (IMP)_objc_msgForward); ...
_impBlockInvoke = (IMP)blockRef->invoke; _blockMethodSignature = [self blockMethodSignature];
- (NSMethodSignature *)blockMethodSignature { const char *signature = [[self blockSignatureStringCTypes] UTF8String]; return [NSMethodSignature signatureWithObjCTypes:signature]; } - (NSString *)blockSignatureStringCTypes { AXBlockStruct *blockRef = (__bridge AXBlockStruct *)_block; const int flags = blockRef->flags; void *signatureLocation = blockRef->descriptor; signatureLocation += sizeof(unsigned long int); signatureLocation += sizeof(unsigned long int); if (flags & AXBlockFlag_HasCopyDispose) { signatureLocation += sizeof(void(*)(void *dst, void *src)); signatureLocation += sizeof(void (*)(void *src)); } const char *signature = (*(const char **)signatureLocation); return [NSString stringWithUTF8String:signature]; }
- (void)forwardInvocation:(NSInvocation *)anInvocation { [anInvocation setTarget:_block]; if (_beforeInvoke) { _beforeInvoke(anInvocation); } IMP imp = _impBlockInvoke; [anInvocation invokeUsingIMP:imp]; }
SEL ax_lambda(id obj, id block, NSMutableArray *lambdas);
SEL ax_lambda(id obj, id block, NSMutableArray *lambdas) { SEL selector = ax_generateFreeSelector(obj); AXProxyBlockWithSelf *proxyBlock = [AXProxyBlockWithSelf initWithBlock:block]; [proxyBlock setBeforeInvoke:^(NSInvocation *invocation){ ax_offsetArgInInvocation(invocation); }]; [lambdas addObject:proxyBlock]; IMP imp = imp_implementationWithBlock(proxyBlock); NSString *signatureString = [proxyBlock blockSignatureStringCTypes]; class_addMethod([obj class], selector, imp, [signatureString UTF8String]); return selector; }
SEL ax_lambda(id obj, id block, NSMutableArray *lambdas); @interface NSObject (AX_Lambda) - (SEL)ax_lambda:(id)block; @end
static char kAX_NSObjectAssociatedObjectKey; @interface NSObject (_AX_Lambda) @property (copy, nonatomic) NSMutableArray *ax_lambdas; @end @implementation NSObject (_AX_Lambda) @dynamic ax_lambdas; - (void)setAx_lambdas:(NSMutableArray *)lambdas { objc_setAssociatedObject(self, &kAX_NSObjectAssociatedObjectKey, lambdas, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSMutableArray *)ax_lambdas { NSMutableArray *marrey = objc_getAssociatedObject(self, &kAX_NSObjectAssociatedObjectKey); if (marrey == nil) { self.ax_lambdas = [NSMutableArray array]; } return objc_getAssociatedObject(self, &kAX_NSObjectAssociatedObjectKey); } @end @implementation NSObject (AX_Lambda) - (SEL)ax_lambda:(id)block { return ax_lambda(self, block, self.ax_lambdas); } @end
SEL ax_generateFreeSelector(id obj) { SEL selector; NSMutableString *mstring = [NSMutableString string]; do { [mstring setString:@"ax_rundom_selector"]; u_int32_t rand = arc4random_uniform(UINT32_MAX); [mstring appendFormat:@"%zd", rand]; selector = NSSelectorFromString(mstring); } while ([obj respondsToSelector:selector]); return selector; }
void ax_offsetArgInInvocation(NSInvocation *invocation) { void *foo = malloc(sizeof(void*)); NSInteger arguments = [[invocation methodSignature] numberOfArguments]; for (NSInteger i = 1; i < arguments-1; i++) { //i = 0 is self [invocation getArgument:foo atIndex:i+1]; [invocation setArgument:foo atIndex:i]; } free(foo); }
NSString *format = @"%@, foo:%@, hello%@"; NSArray *input = @[@(12), @(13), @" world"]; NSString *result = [NSString ax_stringWithFormat:format array:input]; //result — @"12, foo:13, hello world"
+ (instancetype)ax_stringWithFormat:(NSString *)format array:(NSArray *)arguments;
- (instancetype)initWithFormat:(NSString *)format arguments:(va_list)argList NS_FORMAT_FUNCTION(1,0);
+ (instancetype)ax_string:(NSString *)format, ... { va_list list; va_start(list, format); NSString *str = [[NSString alloc] initWithFormat:format arguments:list]; va_end(list); return str; }
You can’t support the use of variable numbers.
+ (NSMethodSignature *)ax_generateSignatureForArguments:(NSArray *)arguments { NSInteger count = [arguments count]; NSInteger sizeptr = sizeof(void *); NSInteger sumArgInvoke = count + 3; // self + _cmd + NSInteger offsetReturnType = sumArgInvoke * sizeptr; NSMutableString *mstring = [[NSMutableString alloc] init]; [mstring appendFormat:@"@%zd@0:%zd", offsetReturnType, sizeptr]; for (NSInteger i = 2; i < sumArgInvoke; i++) { [mstring appendFormat:@"@%zd", sizeptr * i]; } return [NSMethodSignature signatureWithObjCTypes:[mstring UTF8String]]; }
+ (instancetype)ax_stringWithFormat:(NSString *)format array:(NSArray *)arrayArguments { NSMethodSignature *methodSignature = [self ax_generateSignatureForArguments:arrayArguments]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [invocation setTarget:self]; [invocation setSelector:@selector(ax_string:)]; [invocation setArgument:&format atIndex:2]; for (NSInteger i = 0; i < [arrayArguments count]; i++) { id obj = arrayArguments[i]; [invocation setArgument:(&obj) atIndex:i+3]; } [invocation invoke]; __autoreleasing NSString *string; [invocation getReturnValue:&string]; return string; }
+ (instancetype)ax_stringWithFormat:(NSString *)format array:(NSArray *)arrayArguments { NSMethodSignature *methodSignature = [self ax_generateSignatureForArguments:arrayArguments]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [invocation setTarget:self]; [invocation setSelector:@selector(stringWithFormat:)]; [invocation setArgument:&format atIndex:2]; for (NSInteger i = 0; i < [arrayArguments count]; i++) { id obj = arrayArguments[i]; [invocation setArgument:(&obj) atIndex:i+3]; } [invocation invoke]; __autoreleasing NSString *string; [invocation getReturnValue:&string]; return string; } //https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html + (NSMethodSignature *)ax_generateSignatureForArguments:(NSArray *)arguments { NSInteger count = [arguments count]; NSInteger sizeptr = sizeof(void *); NSInteger sumArgInvoke = count + 3; //self + _cmd + (NSString *)format NSInteger offsetReturnType = sumArgInvoke * sizeptr; NSMutableString *mstring = [[NSMutableString alloc] init]; [mstring appendFormat:@"@%zd@0:%zd", offsetReturnType, sizeptr]; for (NSInteger i = 2; i < sumArgInvoke; i++) { [mstring appendFormat:@"@%zd", sizeptr * i]; } return [NSMethodSignature signatureWithObjCTypes:[mstring UTF8String]]; }
- (NSString *)blockSignatureStringCTypes { NSString *signature = [super blockSignatureStringCTypes]; NSString *unformatObject = [signature ax_unformatDec]; NSString *formatNewSignature = [self addSelfToFormat:unformatObject]; NSArray *byteSignature = [signature ax_numbers]; NSArray *byteNewSignature = [self changeByteSignature:byteSignature]; return [NSString ax_stringWithFormat:formatNewSignature array:byteNewSignature]; }
- (NSString *)ax_unformatDec { NSCharacterSet *characterSet = [NSCharacterSet decimalDigitCharacterSet]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"length > 0"]; NSArray *separated = [[self componentsSeparatedByCharactersInSet:characterSet] filteredArrayUsingPredicate:predicate]; NSString *format = [separated componentsJoinedByString:@"%@"]; if ([[self lastSubstring] isEqualToString:[format lastSubstring]] ) { return format; } else { return [format stringByAppendingString:@"%@"]; } } - (NSString *)lastSubstring { NSInteger lastIndex = [self length] - 1; return [self substringFromIndex:lastIndex]; }
- (NSString *)addSelfToFormat:(NSString *)format { NSMutableArray *marray = [[format componentsSeparatedByString:@"?"] mutableCopy]; [marray insertObject:@"?%@@" atIndex:1]; return [marray componentsJoinedByString:@""]; }
- (NSArray *)ax_numbers { NSString *pattern = @"\\d+"; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil]; NSRange fullRange = NSMakeRange(0, [self length]); NSArray *matches = [regex matchesInString:self options:NSMatchingReportProgress range:fullRange]; NSMutableArray *numbers = [NSMutableArray array]; for (NSTextCheckingResult *checkingResult in matches) { NSRange range = [checkingResult range]; NSString *numberStr = [self substringWithRange:range]; NSNumber *number = @([numberStr integerValue]); [numbers addObject:number]; } return numbers; }
- (NSArray *)changeByteSignature:(NSArray *)byteSignature { NSInteger value = sizeof(void *); NSMutableArray *marray = [NSMutableArray array]; for (NSNumber *number in byteSignature) { NSInteger offset = [number integerValue] + value; [marray addObject:@(offset)]; } [marray insertObject:@0 atIndex:1]; return marray; }
Source: https://habr.com/ru/post/276599/
All Articles