From d24c0167619cef34425ebd46f7aa1c05a2faaa36 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Mon, 16 Feb 2015 20:45:13 +0300 Subject: [PATCH 01/17] New interface --- MMAnonymousClass/NSObject+MMAnonymousClass.h | 47 +++-- MMAnonymousClass/NSObject+MMAnonymousClass.m | 194 ++++++------------- 2 files changed, 89 insertions(+), 152 deletions(-) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.h b/MMAnonymousClass/NSObject+MMAnonymousClass.h index 3f2003e..1c3411b 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.h +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.h @@ -10,29 +10,34 @@ #define MM_DEFAULT_REUSE_ID [NSString stringWithFormat:@"%s_%d", __PRETTY_FUNCTION__, __LINE__] -// Excceptions +// ANON_C(NSObject, ^(Class class) { ... }) +#define ANON_C(class, block) \ + [class subclassWithReuseID:MM_DEFAULT_REUSE_ID configBlock:(block)] + +// ANON(^(Class class) { ... }) +#define ANON(block) \ + ANON_C(NSObject, (block)) + +// ANONOBJ_C(NSObject, ^(Class class) { ... }) +#define ANONOBJ_C(class, block) \ + ((id)[[ANON_C(class, block) alloc] init]) + +// ANONOBJ(^(Class class) { ... }) +#define ANONOBJ(block) \ + ((id)[[ANON(block) alloc] init]) + extern NSString *const kMMExeptionMethodError; extern NSString *const kMMExeptionSelector; -// C help functions -extern inline BOOL OVERRIDE(SEL sel, id blockIMP); -extern inline BOOL ADD_METHOD(SEL sel, Protocol *p, id blockIMP); -extern inline BOOL ADD_METHOD_C(SEL sel, Class c, id blockIMP); - -// Category -@interface NSObject(MMAnonymousClass) - -// MARK: - DEPRECATED! -- (id)modifyMethods:(void(^)())blockOv __attribute__((deprecated)); -- (id)addMethod:(SEL)sel fromProtocol:(Protocol *)p isRequired:(BOOL)isReq blockImp:(id)block __attribute__((deprecated)); -- (id)overrideMethod:(SEL)sel blockImp:(id)block __attribute__((deprecated)); -- (IMP)removeInstanceMethod:(SEL)sel; - -// MARK: - Allowed -+ (id)allocAnon:(void(^)())blockOv __attribute__((deprecated)); -+ (id)allocAnonWithReuserID:(NSString*)reuseID :(void(^)())blockOv; -+ (id)newInstAnon:(void(^)())blockOv __attribute__((deprecated)); -+ (id)newInstAnonWithReuseID:(NSString*)reuseID :(void(^)())blockOv; -+ (Class)anonWithReuserID:(NSString*)reuseID; +@interface NSObject (MMAnonymousClass) + ++ (Class)subclassWithReuseID:(NSString *)reuseID + configBlock:(void(^)(Class))block; + ++ (void)addMethod:(SEL)sel fromProtocol:(Protocol *)p blockImp:(id)block; ++ (void)addClassMethod:(SEL)sel blockImp:(id)block; ++ (void)overrideMethod:(SEL)sel blockImp:(id)block; ++ (void)removeMethod:(SEL)sel; ++ (void)removeClassMethod:(SEL)sel; @end diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.m b/MMAnonymousClass/NSObject+MMAnonymousClass.m index b2d410d..7ce064b 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.m +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.m @@ -13,158 +13,90 @@ NSString *const kMMExeptionMethodError = @"MMExeptionMethodError"; NSString *const kMMExeptionSelector = @"MMExeptionSelector"; -static Class newClass = nil; -static BOOL mm_error_flag = NO; +@implementation NSObject (MMAnonymousClass) -inline BOOL OVERRIDE(SEL sel, id blockIMP) { - if (newClass) { - Method method = class_getInstanceMethod(newClass, sel); - if (method) { - class_replaceMethod(newClass, sel, imp_implementationWithBlock(blockIMP), method_getTypeEncoding(method)); - return YES; - } ++ (Class)subclassWithReuseID:(NSString *)reuseID + configBlock:(void(^)(Class class))block +{ + reuseID = [[reuseID componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; + + Class ret = NSClassFromString(reuseID); + if (ret == nil) { + ret = objc_allocateClassPair([self class], reuseID.UTF8String, 0); + block(ret); + objc_registerClassPair(ret); } - //method can't be overrided. Please, check params - mm_error_flag = YES; - NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be overrided. Please, check params", NSStringFromSelector(sel)]; - @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; + return ret; } -inline static BOOL ADD_METHOD_IN(SEL sel,const char *types, id blockIMP){ - if ((newClass) && (types)) { - Method method = class_getInstanceMethod(newClass, sel); - if (method) { - if (OVERRIDE(sel, blockIMP)) - return YES; - } else { - IMP newImp = imp_implementationWithBlock(blockIMP); - if (class_addMethod(newClass, sel, newImp, types)) - return YES; ++ (void)addMethod:(SEL)sel fromProtocol:(Protocol *)p blockImp:(id)block { + struct objc_method_description descript; + for (NSNumber *b in @[@NO,@YES]) { + descript = protocol_getMethodDescription(p, sel, b.boolValue, YES); + if (descript.types) { + [self addMethod:sel blockImp:block types:descript.types]; + return; } } - //method can't be added. Please, check params - mm_error_flag = YES; - NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be added. Please, check params",NSStringFromSelector(sel)]; + NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be found. Please, check protocol",NSStringFromSelector(sel)]; @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } -inline BOOL ADD_METHOD(SEL sel, Protocol *p, id blockIMP) { - struct objc_method_description descript = protocol_getMethodDescription(p, sel, NO, YES); - if (!descript.types) - descript = protocol_getMethodDescription(p, sel, YES, YES); - return ADD_METHOD_IN(sel, descript.types, blockIMP); ++ (void)addClassMethod:(SEL)sel blockImp:(id)block { + Method method = class_getInstanceMethod(self, sel); + [self addMethod:sel blockImp:block types:method_getTypeEncoding(method)]; } -inline BOOL ADD_METHOD_C(SEL sel, Class c, id blockIMP) { - Method method = class_getInstanceMethod(c, sel); - return ADD_METHOD_IN(sel, method_getTypeEncoding(method), blockIMP); -} - -@implementation NSObject (MMAnonymousClass) - -+ (Class)anonClass:(void(^)())blockOv reuseID:(NSString*)reuseID { - //universal mutex ????? - @synchronized([NSObject class]){ - - //частично использован код Sergey Starukhin - //из форка см. https://github.com/pingvin4eg/MMMutableMethods - newClass = nil; - NSString *objClassStr = NSStringFromClass([self class]); - NSString *FORMAT = @"%@_anon_%@"; - NSString *newClassStr = nil; - if (reuseID) { - reuseID = [[reuseID componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; - newClassStr = [NSString stringWithFormat:FORMAT,objClassStr,reuseID]; - newClass = NSClassFromString(newClassStr); - if (newClass) { - mm_error_flag = NO; - blockOv(); - if (mm_error_flag) - return NULL; - return newClass; - } - if (blockOv == nil) - return newClass; - } else { - NSUInteger i = 0; - do { - newClassStr = [NSString stringWithFormat:FORMAT,objClassStr,[@(i) stringValue]]; - newClass = NSClassFromString(newClassStr); - i++; - } while(newClass); - } - - newClass = objc_allocateClassPair([self class], [newClassStr UTF8String], 0); - if (!newClass) - return newClass; - - mm_error_flag = NO; - blockOv(); - if (mm_error_flag) - return NULL; - objc_registerClassPair(newClass); - return newClass; ++ (void)addMethod:(SEL)sel blockImp:(id)block types:(const char *)types { + Method method = class_getInstanceMethod(self, sel); + if (method) { + [self overrideMethod:sel blockImp:block]; + return; + } else { + IMP newImp = imp_implementationWithBlock(block); + if (class_addMethod(self, sel, newImp, types)) + return; } + + NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be added. Please, check params",NSStringFromSelector(sel)]; + @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } -// MARK: - Primary - -+ (id)allocAnon:(void(^)())blockOv { - return [self allocAnonWithReuserID:nil :blockOv]; -} - -+ (id)allocAnonWithReuserID:(NSString*)reuseID :(void(^)())blockOv{ - Class newClass = [self anonClass:blockOv reuseID:reuseID]; - id inst = [newClass alloc]; - newClass = nil; - return inst; -} - -+ (id)newInstAnon:(void(^)())blockOv { - return [self newInstAnonWithReuseID:nil :blockOv]; -} - -+ (id)newInstAnonWithReuseID:(NSString*)reuseID :(void(^)())blockOv { - return [[[self class] allocAnonWithReuserID:reuseID :blockOv] init]; -} - -+ (Class)anonWithReuserID:(NSString*)reuseID { - return [self anonClass:nil reuseID:reuseID]; -} - -// MARK: - Deprecated! - -- (id)modifyMethods:(void(^)())blockOv { - Class newClass = [[self class] anonClass:blockOv reuseID:nil]; - object_setClass(self, newClass); - return self; -} - -- (id)addMethod:(SEL)sel fromProtocol:(Protocol *)p isRequired:(BOOL)isReq blockImp:(id)block { - return [self modifyMethods:^{ - ADD_METHOD(sel, p, block); - }]; ++ (void)overrideMethod:(SEL)sel blockImp:(id)block { + Method method = class_getInstanceMethod(self, sel); + if (method) { + class_replaceMethod(self, sel, imp_implementationWithBlock(block), method_getTypeEncoding(method)); + return; + } + + NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be added. Please, check params",NSStringFromSelector(sel)]; + @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } -- (id)overrideMethod:(SEL)sel blockImp:(id)block { - return [self modifyMethods:^{ - OVERRIDE(sel, block); - }]; ++ (void)removeMethod:(SEL)sel +{ + Method method = class_getClassMethod(self, sel); + if (method != nil) { + method_setImplementation(method,(IMP)_objc_msgForward); + return; + } + + NSString *reason = [NSString stringWithFormat: @"Method (%@) can't be removed. Method not found",NSStringFromSelector(sel)]; + @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } -- (IMP)removeInstanceMethod:(SEL)sel { - Class clas = [self class]; - Method method = class_getInstanceMethod(clas, sel); - if (!method) { - @throw [NSException exceptionWithName:kMMExeptionMethodError - reason:[NSString stringWithFormat: @"Method not found: %@",NSStringFromSelector(sel)] - userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; - return nil; ++ (void)removeClassMethod:(SEL)sel +{ + Method method = class_getInstanceMethod(self, sel); + if (method != nil) { + method_setImplementation(method,(IMP)_objc_msgForward); + return; } - IMP oldImpl = method_setImplementation(method,(IMP)_objc_msgForward); - return oldImpl; + + NSString *reason = [NSString stringWithFormat: @"Method (%@) can't be removed. Method not found",NSStringFromSelector(sel)]; + @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } @end From 6836b79f2f79d60781544e6969db805e6d73cd08 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Tue, 17 Feb 2015 23:56:28 +0300 Subject: [PATCH 02/17] Defines changed with functions to xcode breakpoint works fine inside of methods of anons --- MMAnonymousClass/NSObject+MMAnonymousClass.h | 23 +++++--------------- MMAnonymousClass/NSObject+MMAnonymousClass.m | 11 ++++++++++ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.h b/MMAnonymousClass/NSObject+MMAnonymousClass.h index 1c3411b..9720cb7 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.h +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.h @@ -8,27 +8,14 @@ #import -#define MM_DEFAULT_REUSE_ID [NSString stringWithFormat:@"%s_%d", __PRETTY_FUNCTION__, __LINE__] - -// ANON_C(NSObject, ^(Class class) { ... }) -#define ANON_C(class, block) \ - [class subclassWithReuseID:MM_DEFAULT_REUSE_ID configBlock:(block)] - -// ANON(^(Class class) { ... }) -#define ANON(block) \ - ANON_C(NSObject, (block)) - -// ANONOBJ_C(NSObject, ^(Class class) { ... }) -#define ANONOBJ_C(class, block) \ - ((id)[[ANON_C(class, block) alloc] init]) - -// ANONOBJ(^(Class class) { ... }) -#define ANONOBJ(block) \ - ((id)[[ANON(block) alloc] init]) - extern NSString *const kMMExeptionMethodError; extern NSString *const kMMExeptionSelector; +#define MM_REUSE [NSString stringWithFormat:@"%s_%d", __PRETTY_FUNCTION__, __LINE__] + +Class MM_ANON_CLASS(NSString *reuseID, Class superclass, id block); +id MM_ANON(NSString *reuseID, id block); + @interface NSObject (MMAnonymousClass) + (Class)subclassWithReuseID:(NSString *)reuseID diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.m b/MMAnonymousClass/NSObject+MMAnonymousClass.m index 7ce064b..f834fc3 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.m +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.m @@ -13,6 +13,17 @@ NSString *const kMMExeptionMethodError = @"MMExeptionMethodError"; NSString *const kMMExeptionSelector = @"MMExeptionSelector"; +Class MM_ANON_CLASS(NSString *reuseID, Class superclass, id block) +{ + return [superclass subclassWithReuseID:reuseID configBlock:block]; +} + +id MM_ANON(NSString *reuseID, id block) +{ + Class class = MM_ANON_CLASS(reuseID, [NSObject class], block); + return [[class alloc] init]; +} + @implementation NSObject (MMAnonymousClass) + (Class)subclassWithReuseID:(NSString *)reuseID From 31e5e418337a686d24c3f77a053b6134ed285635 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Wed, 18 Feb 2015 00:01:43 +0300 Subject: [PATCH 03/17] Update and rename Readme.rtf to Readme.md --- Readme.md | 27 +++++++++++++++++++++++++++ Readme.rtf | 7 ------- 2 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 Readme.md delete mode 100644 Readme.rtf diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..88329c7 --- /dev/null +++ b/Readme.md @@ -0,0 +1,27 @@ +MMMutableMethods +======== + +##Installation + +``` +pod 'MMMutableMethods', :branch => 'dev' +``` + +##Usage + +Following code will create new NSObject subclass and add methods **once**, no matter how many times this code will execute. + +``` +[cmd startWithAMCommandCallback:MM_ANON(MM_REUSE,^(Class class){ + [class addMethod:@selector(onResultWithId:) + fromProtocol:@protocol(AMCommandCallback) + blockImp:^(id this,id res){ + NSLog(@"onResultWithId: %@",res); + }]; + [class addMethod:@selector(onErrorWithJavaLangException:) + fromProtocol:@protocol(AMCommandCallback) + blockImp:^(id this,JavaLangException *e){ + NSLog(@"onErrorWithJavaLangException: %@",e); + }]; +})]; +``` diff --git a/Readme.rtf b/Readme.rtf deleted file mode 100644 index fdc424a..0000000 --- a/Readme.rtf +++ /dev/null @@ -1,7 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf510 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0 -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural - -\f0\fs24 \cf0 Empty} \ No newline at end of file From 25bfd548fd77a1a9ec34b3fb9c7bc102016ddb02 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Wed, 18 Feb 2015 00:04:55 +0300 Subject: [PATCH 04/17] Update and rename Readme.md to README.MD --- Readme.md => README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Readme.md => README.MD (88%) diff --git a/Readme.md b/README.MD similarity index 88% rename from Readme.md rename to README.MD index 88329c7..2295e5d 100644 --- a/Readme.md +++ b/README.MD @@ -4,7 +4,7 @@ MMMutableMethods ##Installation ``` -pod 'MMMutableMethods', :branch => 'dev' +pod 'MMMutableMethods', :git => 'https://github.com/k06a/MMMutableMethods', :branch => 'dev' ``` ##Usage From 9739af1e58e5257a95fb815a97b77e96b1222cd4 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Wed, 18 Feb 2015 00:33:00 +0300 Subject: [PATCH 05/17] Update README.MD --- README.MD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.MD b/README.MD index 2295e5d..7154fe0 100644 --- a/README.MD +++ b/README.MD @@ -1,6 +1,8 @@ MMMutableMethods ======== +Simple library to declare anonymous classes in Objective-C runtime, dynamically add methods and class methods. + ##Installation ``` From 497cdb8e2e8d4bec5f61cb64e2a0a7ffba09ed96 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Wed, 18 Feb 2015 00:33:46 +0300 Subject: [PATCH 06/17] Update MMMutableMethods.podspec --- MMMutableMethods.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MMMutableMethods.podspec b/MMMutableMethods.podspec index f16359b..f85373c 100644 --- a/MMMutableMethods.podspec +++ b/MMMutableMethods.podspec @@ -20,8 +20,8 @@ Pod::Spec.new do |s| s.summary = "Anonymous classes for Objective-C" s.description = <<-DESC - Simple library to declare anonimous classes in Objective-C runtime, - override methods for instances of classes and add methods for instances of classes. + Simple library to declare anonymous classes in Objective-C + runtime, dynamically add methods and class methods. DESC s.homepage = "https://github.com/Flanker4/MMMutableMethods" From dac836105149016a2563a26520ff6560fa3a2d76 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Wed, 18 Feb 2015 00:40:29 +0300 Subject: [PATCH 07/17] Changed functions signature due autocomplete issue --- MMAnonymousClass/NSObject+MMAnonymousClass.h | 4 ++-- MMAnonymousClass/NSObject+MMAnonymousClass.m | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.h b/MMAnonymousClass/NSObject+MMAnonymousClass.h index 9720cb7..827d809 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.h +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.h @@ -13,8 +13,8 @@ extern NSString *const kMMExeptionSelector; #define MM_REUSE [NSString stringWithFormat:@"%s_%d", __PRETTY_FUNCTION__, __LINE__] -Class MM_ANON_CLASS(NSString *reuseID, Class superclass, id block); -id MM_ANON(NSString *reuseID, id block); +Class MM_ANON_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong Class class)); +id MM_ANON(NSString *reuseID, void(^block)(__strong Class class)); @interface NSObject (MMAnonymousClass) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.m b/MMAnonymousClass/NSObject+MMAnonymousClass.m index f834fc3..7cb0d84 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.m +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.m @@ -13,12 +13,12 @@ NSString *const kMMExeptionMethodError = @"MMExeptionMethodError"; NSString *const kMMExeptionSelector = @"MMExeptionSelector"; -Class MM_ANON_CLASS(NSString *reuseID, Class superclass, id block) +Class MM_ANON_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong Class class)) { return [superclass subclassWithReuseID:reuseID configBlock:block]; } -id MM_ANON(NSString *reuseID, id block) +id MM_ANON(NSString *reuseID, void(^block)(__strong Class class)) { Class class = MM_ANON_CLASS(reuseID, [NSObject class], block); return [[class alloc] init]; From 396520e48cbf68697f04a544ce7656bc9a527c27 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sun, 22 Feb 2015 10:31:15 +0300 Subject: [PATCH 08/17] Some method renames and fixed remove methods behavior --- MMAnonymousClass/NSObject+MMAnonymousClass.h | 10 +++++----- MMAnonymousClass/NSObject+MMAnonymousClass.m | 14 +++++++------- MMMutableMethods.podspec | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.h b/MMAnonymousClass/NSObject+MMAnonymousClass.h index 827d809..0554275 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.h +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.h @@ -13,8 +13,8 @@ extern NSString *const kMMExeptionSelector; #define MM_REUSE [NSString stringWithFormat:@"%s_%d", __PRETTY_FUNCTION__, __LINE__] -Class MM_ANON_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong Class class)); -id MM_ANON(NSString *reuseID, void(^block)(__strong Class class)); +Class MM_CREATE_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong Class class)); +id MM_CREATE(NSString *reuseID, void(^block)(__strong Class class)); @interface NSObject (MMAnonymousClass) @@ -22,9 +22,9 @@ id MM_ANON(NSString *reuseID, void(^block)(__strong Class class)); configBlock:(void(^)(Class))block; + (void)addMethod:(SEL)sel fromProtocol:(Protocol *)p blockImp:(id)block; -+ (void)addClassMethod:(SEL)sel blockImp:(id)block; ++ (void)addMethod:(SEL)sel fromClass:(Class)class blockImp:(id)block; + (void)overrideMethod:(SEL)sel blockImp:(id)block; -+ (void)removeMethod:(SEL)sel; -+ (void)removeClassMethod:(SEL)sel; ++ (void)removeMethod:(SEL)sel __attribute__((deprecated)); ++ (void)removeClassMethod:(SEL)sel __attribute__((deprecated)); @end diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.m b/MMAnonymousClass/NSObject+MMAnonymousClass.m index 7cb0d84..3e544fe 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.m +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.m @@ -13,14 +13,14 @@ NSString *const kMMExeptionMethodError = @"MMExeptionMethodError"; NSString *const kMMExeptionSelector = @"MMExeptionSelector"; -Class MM_ANON_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong Class class)) +Class MM_CREATE_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong Class class)) { return [superclass subclassWithReuseID:reuseID configBlock:block]; } -id MM_ANON(NSString *reuseID, void(^block)(__strong Class class)) +id MM_CREATE(NSString *reuseID, void(^block)(__strong Class class)) { - Class class = MM_ANON_CLASS(reuseID, [NSObject class], block); + Class class = MM_CREATE_CLASS(reuseID, [NSObject class], block); return [[class alloc] init]; } @@ -55,8 +55,8 @@ + (void)addMethod:(SEL)sel fromProtocol:(Protocol *)p blockImp:(id)block { @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } -+ (void)addClassMethod:(SEL)sel blockImp:(id)block { - Method method = class_getInstanceMethod(self, sel); ++ (void)addMethod:(SEL)sel fromClass:(Class)class blockImp:(id)block { + Method method = class_getInstanceMethod(class, sel); [self addMethod:sel blockImp:block types:method_getTypeEncoding(method)]; } @@ -88,7 +88,7 @@ + (void)overrideMethod:(SEL)sel blockImp:(id)block { + (void)removeMethod:(SEL)sel { - Method method = class_getClassMethod(self, sel); + Method method = class_getInstanceMethod(self, sel); if (method != nil) { method_setImplementation(method,(IMP)_objc_msgForward); return; @@ -100,7 +100,7 @@ + (void)removeMethod:(SEL)sel + (void)removeClassMethod:(SEL)sel { - Method method = class_getInstanceMethod(self, sel); + Method method = class_getClassMethod(self, sel); if (method != nil) { method_setImplementation(method,(IMP)_objc_msgForward); return; diff --git a/MMMutableMethods.podspec b/MMMutableMethods.podspec index f85373c..8b8bc50 100644 --- a/MMMutableMethods.podspec +++ b/MMMutableMethods.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "MMMutableMethods" - s.version = "0.0.2" + s.version = "0.0.3" s.summary = "Anonymous classes for Objective-C" s.description = <<-DESC @@ -74,7 +74,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.2" } + s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.3" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # From d2e6c15793820405fe4d038fe3c1ffcfbed59ae9 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sun, 22 Feb 2015 11:48:12 +0300 Subject: [PATCH 09/17] Update README.MD --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 7154fe0..78c22ad 100644 --- a/README.MD +++ b/README.MD @@ -14,7 +14,7 @@ pod 'MMMutableMethods', :git => 'https://github.com/k06a/MMMutableMethods', :bra Following code will create new NSObject subclass and add methods **once**, no matter how many times this code will execute. ``` -[cmd startWithAMCommandCallback:MM_ANON(MM_REUSE,^(Class class){ +[cmd startWithAMCommandCallback:MM_CREATE(MM_REUSE,^(Class class){ [class addMethod:@selector(onResultWithId:) fromProtocol:@protocol(AMCommandCallback) blockImp:^(id this,id res){ From f48d67bd9f74796f81fe35aac0f5c5c9dfad5555 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Mon, 23 Feb 2015 21:55:31 +0300 Subject: [PATCH 10/17] Added methods for always class creation. --- MMAnonymousClass/NSObject+MMAnonymousClass.h | 2 ++ MMAnonymousClass/NSObject+MMAnonymousClass.m | 15 +++++++++++++++ MMMutableMethods.podspec | 4 ++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.h b/MMAnonymousClass/NSObject+MMAnonymousClass.h index 0554275..56c69e3 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.h +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.h @@ -14,7 +14,9 @@ extern NSString *const kMMExeptionSelector; #define MM_REUSE [NSString stringWithFormat:@"%s_%d", __PRETTY_FUNCTION__, __LINE__] Class MM_CREATE_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong Class class)); +Class MM_CREATE_CLASS_ALWAYS(Class superclass, void(^block)(__strong Class class)); id MM_CREATE(NSString *reuseID, void(^block)(__strong Class class)); +id MM_CREATE_ALWAYS(void(^block)(__strong Class class)); @interface NSObject (MMAnonymousClass) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.m b/MMAnonymousClass/NSObject+MMAnonymousClass.m index 3e544fe..19fad89 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.m +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.m @@ -18,18 +18,33 @@ Class MM_CREATE_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong return [superclass subclassWithReuseID:reuseID configBlock:block]; } +Class MM_CREATE_CLASS_ALWAYS(Class superclass, void(^block)(__strong Class class)) +{ + return [superclass subclassWithReuseID:nil configBlock:block]; +} + id MM_CREATE(NSString *reuseID, void(^block)(__strong Class class)) { Class class = MM_CREATE_CLASS(reuseID, [NSObject class], block); return [[class alloc] init]; } +id MM_CREATE_ALWAYS(void(^block)(__strong Class class)) +{ + Class class = MM_CREATE_CLASS_ALWAYS([NSObject class], block); + return [[class alloc] init]; +} + @implementation NSObject (MMAnonymousClass) + (Class)subclassWithReuseID:(NSString *)reuseID configBlock:(void(^)(Class class))block { reuseID = [[reuseID componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; + if (reuseID == nil) { + static NSInteger index = 0; + reuseID = [NSString stringWithFormat:@"MMAnonymousClass%@",@(index++)]; + } Class ret = NSClassFromString(reuseID); if (ret == nil) { diff --git a/MMMutableMethods.podspec b/MMMutableMethods.podspec index 8b8bc50..82d9f19 100644 --- a/MMMutableMethods.podspec +++ b/MMMutableMethods.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "MMMutableMethods" - s.version = "0.0.3" + s.version = "0.0.4" s.summary = "Anonymous classes for Objective-C" s.description = <<-DESC @@ -74,7 +74,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.3" } + s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.4" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # From a9e3a277d322bc64afd8eb09102dc11e1fab20a4 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Tue, 24 Feb 2015 01:51:54 +0300 Subject: [PATCH 11/17] Now _ALWAYS methods deletes class after first usage --- MMAnonymousClass/NSObject+MMAnonymousClass.h | 1 + MMAnonymousClass/NSObject+MMAnonymousClass.m | 13 +++++++++++++ MMMutableMethods.podspec | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.h b/MMAnonymousClass/NSObject+MMAnonymousClass.h index 56c69e3..425bffe 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.h +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.h @@ -28,5 +28,6 @@ id MM_CREATE_ALWAYS(void(^block)(__strong Class class)); + (void)overrideMethod:(SEL)sel blockImp:(id)block; + (void)removeMethod:(SEL)sel __attribute__((deprecated)); + (void)removeClassMethod:(SEL)sel __attribute__((deprecated)); ++ (void)deleteClass; @end diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.m b/MMAnonymousClass/NSObject+MMAnonymousClass.m index 19fad89..ebe9241 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.m +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.m @@ -41,6 +41,7 @@ + (Class)subclassWithReuseID:(NSString *)reuseID configBlock:(void(^)(Class class))block { reuseID = [[reuseID componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; + BOOL nilReuseID = (reuseID == nil); if (reuseID == nil) { static NSInteger index = 0; reuseID = [NSString stringWithFormat:@"MMAnonymousClass%@",@(index++)]; @@ -50,6 +51,11 @@ + (Class)subclassWithReuseID:(NSString *)reuseID if (ret == nil) { ret = objc_allocateClassPair([self class], reuseID.UTF8String, 0); block(ret); + if (nilReuseID) { + [ret addMethod:NSSelectorFromString(@"dealloc") fromClass:[NSObject class] blockImp:^(id this) { + [ret deleteClass]; + }]; + } objc_registerClassPair(ret); } @@ -125,4 +131,11 @@ + (void)removeClassMethod:(SEL)sel @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } ++ (void)deleteClass +{ + dispatch_async(dispatch_get_main_queue(), ^{ + objc_disposeClassPair(self); + }); +} + @end diff --git a/MMMutableMethods.podspec b/MMMutableMethods.podspec index 82d9f19..19b6281 100644 --- a/MMMutableMethods.podspec +++ b/MMMutableMethods.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "MMMutableMethods" - s.version = "0.0.4" + s.version = "0.0.5" s.summary = "Anonymous classes for Objective-C" s.description = <<-DESC @@ -74,7 +74,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.4" } + s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.5" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # From 7331497cf71024702b6122a78ee406bf7c32e0ae Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Tue, 24 Feb 2015 02:23:30 +0300 Subject: [PATCH 12/17] Fixed some bugs --- MMAnonymousClass/NSObject+MMAnonymousClass.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.m b/MMAnonymousClass/NSObject+MMAnonymousClass.m index ebe9241..0ccf186 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.m +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.m @@ -52,8 +52,14 @@ + (Class)subclassWithReuseID:(NSString *)reuseID ret = objc_allocateClassPair([self class], reuseID.UTF8String, 0); block(ret); if (nilReuseID) { - [ret addMethod:NSSelectorFromString(@"dealloc") fromClass:[NSObject class] blockImp:^(id this) { + SEL sel = NSSelectorFromString(@"dealloc"); + IMP imp = class_getMethodImplementation(self, sel); + IMP newImp = imp_implementationWithBlock(^(id this) { [ret deleteClass]; + }); + [ret addMethod:sel fromClass:[NSObject class] blockImp:^(id this){ + ((void(*)(id))newImp)(this); + ((void(*)(id))imp)(this); }]; } objc_registerClassPair(ret); From 29af4553e4245ddd8089f0e865d7af778f1f08f4 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Tue, 24 Feb 2015 13:33:42 +0300 Subject: [PATCH 13/17] Simplified addMethod with optional overriden --- MMAnonymousClass/NSObject+MMAnonymousClass.h | 4 +- MMAnonymousClass/NSObject+MMAnonymousClass.m | 40 +++++++++----------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.h b/MMAnonymousClass/NSObject+MMAnonymousClass.h index 425bffe..002521b 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.h +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.h @@ -11,7 +11,7 @@ extern NSString *const kMMExeptionMethodError; extern NSString *const kMMExeptionSelector; -#define MM_REUSE [NSString stringWithFormat:@"%s_%d", __PRETTY_FUNCTION__, __LINE__] +#define MM_REUSE [NSString stringWithFormat:@"MMAnonymousClass_%s_%d", __PRETTY_FUNCTION__, __LINE__] Class MM_CREATE_CLASS(NSString *reuseID, Class superclass, void(^block)(__strong Class class)); Class MM_CREATE_CLASS_ALWAYS(Class superclass, void(^block)(__strong Class class)); @@ -23,7 +23,7 @@ id MM_CREATE_ALWAYS(void(^block)(__strong Class class)); + (Class)subclassWithReuseID:(NSString *)reuseID configBlock:(void(^)(Class))block; -+ (void)addMethod:(SEL)sel fromProtocol:(Protocol *)p blockImp:(id)block; ++ (void)addMethod:(SEL)sel fromProtocol:(Protocol *)proto blockImp:(id)block; + (void)addMethod:(SEL)sel fromClass:(Class)class blockImp:(id)block; + (void)overrideMethod:(SEL)sel blockImp:(id)block; + (void)removeMethod:(SEL)sel __attribute__((deprecated)); diff --git a/MMAnonymousClass/NSObject+MMAnonymousClass.m b/MMAnonymousClass/NSObject+MMAnonymousClass.m index 0ccf186..3a3ba81 100644 --- a/MMAnonymousClass/NSObject+MMAnonymousClass.m +++ b/MMAnonymousClass/NSObject+MMAnonymousClass.m @@ -57,7 +57,7 @@ + (Class)subclassWithReuseID:(NSString *)reuseID IMP newImp = imp_implementationWithBlock(^(id this) { [ret deleteClass]; }); - [ret addMethod:sel fromClass:[NSObject class] blockImp:^(id this){ + [ret overrideMethod:sel blockImp:^(id this){ ((void(*)(id))newImp)(this); ((void(*)(id))imp)(this); }]; @@ -68,40 +68,36 @@ + (Class)subclassWithReuseID:(NSString *)reuseID return ret; } -+ (void)addMethod:(SEL)sel fromProtocol:(Protocol *)p blockImp:(id)block { - struct objc_method_description descript; - for (NSNumber *b in @[@NO,@YES]) { - descript = protocol_getMethodDescription(p, sel, b.boolValue, YES); - if (descript.types) { - [self addMethod:sel blockImp:block types:descript.types]; - return; - } ++ (void)addMethod:(SEL)sel fromProtocol:(Protocol *)proto blockImp:(id)block { + struct objc_method_description descript = protocol_getMethodDescription(proto, sel, NO, YES); + if (descript.types == nil) + descript = protocol_getMethodDescription(proto, sel, YES, YES); + if (descript.types) { + [self addMethod:sel blockImp:block types:descript.types]; + return; } - NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be found. Please, check protocol",NSStringFromSelector(sel)]; + NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be found. Please, check %@ protocol",NSStringFromSelector(sel),NSStringFromProtocol(proto)]; @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } + (void)addMethod:(SEL)sel fromClass:(Class)class blockImp:(id)block { Method method = class_getInstanceMethod(class, sel); - [self addMethod:sel blockImp:block types:method_getTypeEncoding(method)]; -} - -+ (void)addMethod:(SEL)sel blockImp:(id)block types:(const char *)types { - Method method = class_getInstanceMethod(self, sel); if (method) { - [self overrideMethod:sel blockImp:block]; + const char *types = method_getTypeEncoding(method); + [self addMethod:sel blockImp:block types:types]; return; - } else { - IMP newImp = imp_implementationWithBlock(block); - if (class_addMethod(self, sel, newImp, types)) - return; } - NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be added. Please, check params",NSStringFromSelector(sel)]; + NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be found. Please, check %@ class",NSStringFromSelector(sel),NSStringFromClass(class)]; @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } ++ (void)addMethod:(SEL)sel blockImp:(id)block types:(const char *)types { + IMP newImp = imp_implementationWithBlock(block); + class_replaceMethod(self, sel, newImp, types); +} + + (void)overrideMethod:(SEL)sel blockImp:(id)block { Method method = class_getInstanceMethod(self, sel); if (method) { @@ -109,7 +105,7 @@ + (void)overrideMethod:(SEL)sel blockImp:(id)block { return; } - NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be added. Please, check params",NSStringFromSelector(sel)]; + NSString *reason = [NSString stringWithFormat:@"Method (%@) can't be overriden. It does not exists",NSStringFromSelector(sel)]; @throw [NSException exceptionWithName:kMMExeptionMethodError reason:reason userInfo:@{kMMExeptionSelector:NSStringFromSelector(sel)}]; } From c82d93547043d74f14b70cff1c870dc28a50b06c Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Tue, 24 Feb 2015 13:34:11 +0300 Subject: [PATCH 14/17] 0.0.6 --- MMMutableMethods.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MMMutableMethods.podspec b/MMMutableMethods.podspec index 19b6281..1cc84fa 100644 --- a/MMMutableMethods.podspec +++ b/MMMutableMethods.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "MMMutableMethods" - s.version = "0.0.5" + s.version = "0.0.6" s.summary = "Anonymous classes for Objective-C" s.description = <<-DESC @@ -74,7 +74,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.5" } + s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.6" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # From 6e786ea791cd0d7207111cbb0e239eb43b98b5cc Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Tue, 24 Feb 2015 20:16:02 +0300 Subject: [PATCH 15/17] Added MMAnonymousClass with dynamic methods functionality without runtime usage --- MMAnonymousClass/MMAnonymousClass.h | 21 +++++ MMAnonymousClass/MMAnonymousClass.m | 123 ++++++++++++++++++++++++++++ MMMutableMethods.podspec | 4 +- 3 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 MMAnonymousClass/MMAnonymousClass.h create mode 100644 MMAnonymousClass/MMAnonymousClass.m diff --git a/MMAnonymousClass/MMAnonymousClass.h b/MMAnonymousClass/MMAnonymousClass.h new file mode 100644 index 0000000..b091f71 --- /dev/null +++ b/MMAnonymousClass/MMAnonymousClass.h @@ -0,0 +1,21 @@ +// +// MMAnonymousClass.h +// Runtime +// +// Created by Anton Bukov on 24.02.15. +// Copyright (c) 2015 Anton Bukov. All rights reserved. +// + +#import + +@class MMAnonymousClass; +id MM_ANON(void(^block)(MMAnonymousClass *anon)); + +@interface MMAnonymousClass : NSObject + ++ (MMAnonymousClass *)anonWithBlock:(void(^)(MMAnonymousClass *anon))block; + +- (void)addMethod:(SEL)sel fromProtocol:(Protocol *)proto blockImp:(id)block; +- (void)addMethod:(SEL)sel fromClass:(Class)class blockImp:(id)block; + +@end diff --git a/MMAnonymousClass/MMAnonymousClass.m b/MMAnonymousClass/MMAnonymousClass.m new file mode 100644 index 0000000..17ac38b --- /dev/null +++ b/MMAnonymousClass/MMAnonymousClass.m @@ -0,0 +1,123 @@ +// +// MMAnonymousClass.m +// Runtime +// +// Created by Anton Bukov on 24.02.15. +// Copyright (c) 2015 Anton Bukov. All rights reserved. +// + +#import +#import +#import "MMAnonymousClass.h" + +id MM_ANON(void(^block)(MMAnonymousClass *anon)) +{ + return [MMAnonymousClass anonWithBlock:block]; +} + +@interface MMAnonymousClass () + +@property (nonatomic, strong) NSMutableDictionary *blocks; +@property (nonatomic, strong) NSMutableDictionary *protocols; +@property (nonatomic, strong) NSMutableDictionary *classes; + +@end + +@implementation MMAnonymousClass + +- (NSMutableDictionary *)blocks +{ + if (_blocks == nil) + _blocks = [NSMutableDictionary dictionary]; + return _blocks; +} + +- (NSMutableDictionary *)protocols +{ + if (_protocols == nil) + _protocols = [NSMutableDictionary dictionary]; + return _protocols; +} + +- (NSMutableDictionary *)classes +{ + if (_classes == nil) + _classes = [NSMutableDictionary dictionary]; + return _classes; +} + +- (const char *)typesForSelector:(SEL)sel +{ + NSString *protoStr = self.protocols[NSStringFromSelector(sel)]; + if (protoStr) { + Protocol *proto = NSProtocolFromString(protoStr); + struct objc_method_description descript = protocol_getMethodDescription(proto, sel, NO, YES); + if (descript.types == nil) + descript = protocol_getMethodDescription(proto, sel, YES, YES); + return descript.types; + } + + NSString *classStr = self.classes[NSStringFromSelector(sel)]; + if (classStr) { + Class class = NSClassFromString(classStr); + Method method = class_getInstanceMethod(class, sel); + if (method) + return method_getTypeEncoding(method); + } + + return nil; +} + +#pragma mark - Public Methods + ++ (MMAnonymousClass *)anonWithBlock:(void(^)(MMAnonymousClass *anon))block +{ + MMAnonymousClass *anon = [[MMAnonymousClass alloc] init]; + block(anon); + return anon; +} + +- (void)addMethod:(SEL)sel fromProtocol:(Protocol *)proto blockImp:(id)block +{ + NSString *selStr = NSStringFromSelector(sel); + self.blocks[selStr] = block; + self.protocols[selStr] = NSStringFromProtocol(proto); +} + +- (void)addMethod:(SEL)sel fromClass:(Class)class blockImp:(id)block +{ + NSString *selStr = NSStringFromSelector(sel); + self.blocks[selStr] = block; + self.classes[selStr] = NSStringFromClass(class); +} + +#pragma mark - Messsage Forwarding + +- (BOOL)respondsToSelector:(SEL)aSelector +{ + for (NSString *key in self.blocks) + if (NSSelectorFromString(key) == aSelector) + return YES; + return [super respondsToSelector:aSelector]; +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector +{ + const char *types = [self typesForSelector:aSelector]; + return [NSMethodSignature signatureWithObjCTypes:types]; +} + +- (void)forwardInvocation:(NSInvocation *)anInvocation +{ + for (NSString *key in self.blocks) { + if (NSSelectorFromString(key) == anInvocation.selector) { + id block = self.blocks[key]; + anInvocation.selector = nil; + [anInvocation invokeWithTarget:block]; + return; + } + } + return [super forwardInvocation:anInvocation]; +} + +@end diff --git a/MMMutableMethods.podspec b/MMMutableMethods.podspec index 1cc84fa..34f2ae9 100644 --- a/MMMutableMethods.podspec +++ b/MMMutableMethods.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "MMMutableMethods" - s.version = "0.0.6" + s.version = "0.0.7" s.summary = "Anonymous classes for Objective-C" s.description = <<-DESC @@ -74,7 +74,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.6" } + s.source = { :git => "https://github.com/Flanker4/MMMutableMethods.git", :tag => "0.0.7" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # From a24e3a0ac485e7a129b4d36db660d530a28c52d4 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Fri, 6 Mar 2015 16:14:36 +0300 Subject: [PATCH 16/17] Update README.MD --- README.MD | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.MD b/README.MD index 78c22ad..e0b721b 100644 --- a/README.MD +++ b/README.MD @@ -14,16 +14,16 @@ pod 'MMMutableMethods', :git => 'https://github.com/k06a/MMMutableMethods', :bra Following code will create new NSObject subclass and add methods **once**, no matter how many times this code will execute. ``` -[cmd startWithAMCommandCallback:MM_CREATE(MM_REUSE,^(Class class){ - [class addMethod:@selector(onResultWithId:) - fromProtocol:@protocol(AMCommandCallback) - blockImp:^(id this,id res){ - NSLog(@"onResultWithId: %@",res); - }]; - [class addMethod:@selector(onErrorWithJavaLangException:) - fromProtocol:@protocol(AMCommandCallback) - blockImp:^(id this,JavaLangException *e){ - NSLog(@"onErrorWithJavaLangException: %@",e); - }]; +[cmd startWithAMCommandCallback:MM_ANON(^(MMAnonymousClass *anon){ + [anon addMethod:@selector(onResultWithId:) + fromProtocol:@protocol(AMCommandCallback) + blockImp:^(id this,id res){ + NSLog(@"onResultWithId: %@",res); + }]; + [anon addMethod:@selector(onErrorWithJavaLangException:) + fromProtocol:@protocol(AMCommandCallback) + blockImp:^(id this,JavaLangException *e){ + NSLog(@"onErrorWithJavaLangException: %@",e); + }]; })]; ``` From 9a14c09568555a4545652d803dc928ec25a9ae1a Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Tue, 13 Oct 2015 10:55:33 +0300 Subject: [PATCH 17/17] readme changes --- README.MD | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index e0b721b..32aebfa 100644 --- a/README.MD +++ b/README.MD @@ -6,14 +6,35 @@ Simple library to declare anonymous classes in Objective-C runtime, dynamically ##Installation ``` -pod 'MMMutableMethods', :git => 'https://github.com/k06a/MMMutableMethods', :branch => 'dev' +pod 'MMMutableMethods', :git => 'https://github.com/k06a/MMMutableMethods' ``` ##Usage -Following code will create new NSObject subclass and add methods **once**, no matter how many times this code will execute. +First mechanism works on obj-c runtime class creation: ``` +#import + +MM_CREATE(MM_REUSE,^(Class class){ + [class addMethod:@selector(onResultWithId:) + fromProtocol:@protocol(AMCommandCallback) + blockImp:^(id this,id res){ + NSLog(@"onResultWithId: %@",res); + }]; + [class addMethod:@selector(onErrorWithJavaLangException:) + fromProtocol:@protocol(AMCommandCallback) + blockImp:^(id this,JavaLangException *e){ + NSLog(@"onErrorWithJavaLangException: %@",e); + }]; +}) +``` + +Second mechanism works on simple message forward implementation: + +``` +#import + [cmd startWithAMCommandCallback:MM_ANON(^(MMAnonymousClass *anon){ [anon addMethod:@selector(onResultWithId:) fromProtocol:@protocol(AMCommandCallback) @@ -27,3 +48,9 @@ Following code will create new NSObject subclass and add methods **once**, no ma }]; })]; ``` + +First one crates new obc-j classes in runtime, it allows you to create classes `MM_CREATE_CLASS(MM_REUSE, *)` and directly instances with `MM_CREATE(MM_REUSE, *)`. Classes will be created only on first execution and reused by default, but you can avoid reusing by calling `MM_CREATE_CLASS_ALWAYS(*)` and `MM_CREATE_ALWAYS(*)`. + +The second mechanism doesn't creates any runtime instances, just remember blocks for selectors and forward method calls to them. + +I prefere second way not to create a lot of classes in runtime. IMHO it is much safer and enough powerful. \ No newline at end of file