From adbf412a4e243181ef1a9137c3056aebe0015418 Mon Sep 17 00:00:00 2001 From: Indiegogo Pair Date: Fri, 7 Nov 2014 15:49:24 -0800 Subject: [PATCH 1/4] fix crash while injecting property that implements a protocol --- Source/JSObjectionUtils.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/JSObjectionUtils.m b/Source/JSObjectionUtils.m index f436856..00f6903 100644 --- a/Source/JSObjectionUtils.m +++ b/Source/JSObjectionUtils.m @@ -35,6 +35,10 @@ static JSObjectionPropertyInfo FindClassOrProtocolForProperty(objc_property_t pr classOrProtocol = objc_getProtocol([classOrProtocolName UTF8String]); propertyInfo.type = JSObjectionTypeProtocol; } else { + if ([classOrProtocolName hasSuffix:@">"]) { + classOrProtocolName = [classOrProtocolName substringToIndex:[classOrProtocolName rangeOfString:@"<"].location]; + } + classOrProtocol = NSClassFromString(classOrProtocolName); propertyInfo.type = JSObjectionTypeClass; } From c09f3844560cbb724cd6f2cbb8fa310aeb0680de Mon Sep 17 00:00:00 2001 From: Dave MacLachlan Date: Mon, 23 Nov 2015 15:46:32 -0800 Subject: [PATCH 2/4] Fix up objection so there are no warnings about unknown selectors, and it removes the need to call performSelector when standard dispatch will do. --- Source/JSObjectionInjector.h | 8 ++++++++ Source/JSObjectionInjectorEntry.h | 7 +++++++ Source/JSObjectionInjectorEntry.m | 4 ++-- Source/JSObjectionUtils.m | 10 +++++----- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Source/JSObjectionInjector.h b/Source/JSObjectionInjector.h index f960e0e..c96f471 100644 --- a/Source/JSObjectionInjector.h +++ b/Source/JSObjectionInjector.h @@ -1,6 +1,14 @@ #import #import "JSObjectionModule.h" +@protocol JSObjectionInjectorSelectors + +@optional ++ (NSSet *)objectionRequires; ++ (NSDictionary *)objectionRequiresNames; + +@end + @interface JSObjectionInjector : NSObject - (instancetype)initWithContext:(NSDictionary *)theGlobalContext; diff --git a/Source/JSObjectionInjectorEntry.h b/Source/JSObjectionInjectorEntry.h index 0d31ab0..1d079ec 100644 --- a/Source/JSObjectionInjectorEntry.h +++ b/Source/JSObjectionInjectorEntry.h @@ -1,6 +1,13 @@ #import #import "JSObjectionEntry.h" +@protocol JSObjectionInjectorEntrySelectors + +@optional ++ (id)objectionInitializer; + +@end + @interface JSObjectionInjectorEntry : JSObjectionEntry @property (nonatomic, readonly) Class classEntry; diff --git a/Source/JSObjectionInjectorEntry.m b/Source/JSObjectionInjectorEntry.m index 053e0a7..aea8ece 100644 --- a/Source/JSObjectionInjectorEntry.m +++ b/Source/JSObjectionInjectorEntry.m @@ -73,11 +73,11 @@ - (id)buildObject:(NSArray *)arguments initializer: (SEL) initializer { } - (SEL)initializerForObject { - return NSSelectorFromString([[self.classEntry performSelector:@selector(objectionInitializer)] objectForKey:JSObjectionInitializerKey]); + return NSSelectorFromString([[self.classEntry objectionInitializer] objectForKey:JSObjectionInitializerKey]); } - (NSArray *)argumentsForObject:(NSArray *)givenArguments { - return givenArguments.count > 0 ? givenArguments : [[self.classEntry performSelector:@selector(objectionInitializer)] objectForKey:JSObjectionDefaultArgumentsKey]; + return givenArguments.count > 0 ? givenArguments : [[self.classEntry objectionInitializer] objectForKey:JSObjectionDefaultArgumentsKey]; } diff --git a/Source/JSObjectionUtils.m b/Source/JSObjectionUtils.m index 00f6903..08d75a1 100644 --- a/Source/JSObjectionUtils.m +++ b/Source/JSObjectionUtils.m @@ -54,7 +54,7 @@ static JSObjectionPropertyInfo FindClassOrProtocolForProperty(objc_property_t pr static NSSet* BuildDependenciesForClass(Class klass, NSSet *requirements) { Class superClass = class_getSuperclass([klass class]); if([superClass respondsToSelector:@selector(objectionRequires)]) { - NSSet *parentsRequirements = [superClass performSelector:@selector(objectionRequires)]; + NSSet *parentsRequirements = [superClass objectionRequires]; NSMutableSet *dependencies = [NSMutableSet setWithSet:parentsRequirements]; [dependencies unionSet:requirements]; requirements = dependencies; @@ -65,7 +65,7 @@ static JSObjectionPropertyInfo FindClassOrProtocolForProperty(objc_property_t pr static NSDictionary* BuildNamedDependenciesForClass(Class klass, NSDictionary *namedRequirements) { Class superClass = class_getSuperclass([klass class]); if([superClass respondsToSelector:@selector(objectionRequiresNames)]) { - NSDictionary *parentsNamedRequirements = [superClass performSelector:@selector(objectionRequiresNames)]; + NSDictionary *parentsNamedRequirements = [superClass objectionRequiresNames]; NSMutableDictionary *namedDependencies = [NSMutableDictionary dictionaryWithDictionary:parentsNamedRequirements]; [namedDependencies addEntriesFromDictionary:namedRequirements]; namedRequirements = namedDependencies; @@ -150,7 +150,7 @@ static void _validateObjectReturnedFromInjector(id *theObject, JSObjectionProper static void InjectDependenciesIntoProperties(JSObjectionInjector *injector, Class klass, id object) { if ([klass respondsToSelector:@selector(objectionRequires)]) { - NSSet *properties = [klass performSelector:@selector(objectionRequires)]; + NSSet *properties = [klass objectionRequires]; NSMutableDictionary *propertiesDictionary = [NSMutableDictionary dictionaryWithCapacity:properties.count]; for (NSString *propertyName in properties) { JSObjectionPropertyInfo propertyInfo; @@ -165,7 +165,7 @@ static void InjectDependenciesIntoProperties(JSObjectionInjector *injector, Clas } if ([klass respondsToSelector:@selector(objectionRequiresNames)]) { - NSDictionary *namedProperties = [klass performSelector:@selector(objectionRequiresNames)]; + NSDictionary *namedProperties = [klass objectionRequiresNames]; NSMutableDictionary *propertiesDictionary = [NSMutableDictionary dictionaryWithCapacity:namedProperties.count]; for (NSString *namedPropertyKey in [namedProperties allKeys]) { NSString* propertyName = [namedProperties valueForKey:namedPropertyKey]; @@ -181,7 +181,7 @@ static void InjectDependenciesIntoProperties(JSObjectionInjector *injector, Clas } if ([object respondsToSelector:@selector(awakeFromObjection)]) { - [object performSelector:@selector(awakeFromObjection)]; + [object awakeFromObjection]; } } From d9436f6520590c19db5c44350b945d0d1c9218d1 Mon Sep 17 00:00:00 2001 From: Paul Zabelin Date: Wed, 25 Nov 2015 20:59:45 -0800 Subject: [PATCH 3/4] add spec example of injecting a property that implements a protocol --- Objection.xcodeproj/project.pbxproj | 6 ++++ Specs/InjectImplementsProtocolSpecs.m | 44 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 Specs/InjectImplementsProtocolSpecs.m diff --git a/Objection.xcodeproj/project.pbxproj b/Objection.xcodeproj/project.pbxproj index 0bb450a..1112399 100644 --- a/Objection.xcodeproj/project.pbxproj +++ b/Objection.xcodeproj/project.pbxproj @@ -119,6 +119,8 @@ 4B9D310713DF613600C81C45 /* JSObjection.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B9D310213DF613500C81C45 /* JSObjection.m */; }; 4B9D311413DF79FE00C81C45 /* JSObjectionUtils.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4B9D30FE13DF5FAC00C81C45 /* JSObjectionUtils.h */; }; 4B9D311513DF79FE00C81C45 /* JSObjection.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4B9D310113DF613500C81C45 /* JSObjection.h */; }; + 4BA620D01C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BA620CF1C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m */; }; + 4BA620D11C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BA620CF1C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m */; }; 4BA6DD1F12AAB3CD00CFFD70 /* JSObjectionInjectorEntry.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4BED512F12AA8BF300CA6B36 /* JSObjectionInjectorEntry.h */; }; 4BA6DD2012AAB3CD00CFFD70 /* JSObjectionInjector.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4BED513012AA8BF300CA6B36 /* JSObjectionInjector.h */; }; 4BA6DD2112AAB3CD00CFFD70 /* JSObjectionBindingEntry.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4BED513112AA8BF300CA6B36 /* JSObjectionBindingEntry.h */; }; @@ -325,6 +327,7 @@ 4B9D30FE13DF5FAC00C81C45 /* JSObjectionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSObjectionUtils.h; sourceTree = ""; }; 4B9D310113DF613500C81C45 /* JSObjection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSObjection.h; sourceTree = ""; }; 4B9D310213DF613500C81C45 /* JSObjection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSObjection.m; sourceTree = ""; }; + 4BA620CF1C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InjectImplementsProtocolSpecs.m; sourceTree = ""; }; 4BA9CFF412AA86E600674F0E /* OCHamcrest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OCHamcrest.framework; path = Vendor/OCHamcrest.framework; sourceTree = ""; }; 4BB07D741668679A00D9BA1E /* AddAndRemoveModulesSpecs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddAndRemoveModulesSpecs.m; sourceTree = ""; }; 4BB428A116D3205C00710F1E /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; @@ -551,6 +554,7 @@ 4B95819612AE593F00CBF1EB /* ModuleUsageSpecs.m */, 4B4289301567F3EC004F73D3 /* InitializerSpecs.m */, 4BB07D741668679A00D9BA1E /* AddAndRemoveModulesSpecs.m */, + 4BA620CF1C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m */, ); path = Specs; sourceTree = ""; @@ -880,6 +884,7 @@ 4B42585F13F56D41006BC001 /* InheritanceSpecs.m in Sources */, 4B42586013F56D41006BC001 /* ModuleUsageSpecs.m in Sources */, 4B42586113F56D41006BC001 /* JSObjectionProviderEntry.m in Sources */, + 4BA620D01C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m in Sources */, 4B42586213F56D41006BC001 /* JSObjectionEntry.m in Sources */, 4B42586313F56D41006BC001 /* NSObject+Objection.m in Sources */, 4B42586413F56D41006BC001 /* JSObjectionUtils.m in Sources */, @@ -912,6 +917,7 @@ 4B4258D313F56F45006BC001 /* InheritanceSpecs.m in Sources */, 4B4258D413F56F45006BC001 /* ModuleUsageSpecs.m in Sources */, 4B4258D513F56F45006BC001 /* JSObjectionProviderEntry.m in Sources */, + 4BA620D11C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m in Sources */, 4B4258D613F56F45006BC001 /* JSObjectionEntry.m in Sources */, 4B4258D713F56F45006BC001 /* NSObject+Objection.m in Sources */, 4B4258D813F56F45006BC001 /* JSObjectionUtils.m in Sources */, diff --git a/Specs/InjectImplementsProtocolSpecs.m b/Specs/InjectImplementsProtocolSpecs.m new file mode 100644 index 0000000..ef681ef --- /dev/null +++ b/Specs/InjectImplementsProtocolSpecs.m @@ -0,0 +1,44 @@ +#import "SpecHelper.h" +#import "Fixtures.h" +#import "ModuleFixtures.h" + +@interface RocketEngine : Engine +@end + +@implementation RocketEngine +@synthesize speed; +@end + +@interface RocketScienceModule : JSObjectionModule +@end + +@implementation RocketScienceModule +-(void)configure { + [self bindClass:[RocketEngine class] toClass:[Engine class]]; +} +@end + +@interface ExperimentalCar : NSObject +@property (nonatomic, retain) Engine *engine; +@end + +@implementation ExperimentalCar +objection_requires(@"engine") +@end + +QuickSpecBegin(InjectImplementsProtocolSpecs) + +__block JSObjectionInjector *injector = nil; +__block ExperimentalCar *carWithRocketEngine = nil; + +beforeEach(^{ + injector = [JSObjection createInjector:[RocketScienceModule new]]; + carWithRocketEngine = [ExperimentalCar new]; +}); + +it(@"injects property engine that implements Blinkable and UnregisteredProtocol protocols ", ^{ + [injector injectDependencies:carWithRocketEngine]; + assertThat(carWithRocketEngine.engine, is(instanceOf([RocketEngine class]))); +}); + +QuickSpecEnd \ No newline at end of file From 6b9d539131d249f33e875824692b34e9909b6205 Mon Sep 17 00:00:00 2001 From: Paul Zabelin Date: Wed, 25 Nov 2015 21:13:36 -0800 Subject: [PATCH 4/4] refactor spec to reuse existing fixtures --- Specs/InjectImplementsProtocolSpecs.m | 33 +++++++++++---------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/Specs/InjectImplementsProtocolSpecs.m b/Specs/InjectImplementsProtocolSpecs.m index ef681ef..87f0882 100644 --- a/Specs/InjectImplementsProtocolSpecs.m +++ b/Specs/InjectImplementsProtocolSpecs.m @@ -2,43 +2,36 @@ #import "Fixtures.h" #import "ModuleFixtures.h" -@interface RocketEngine : Engine +@interface NewCarModule : JSObjectionModule @end -@implementation RocketEngine -@synthesize speed; -@end - -@interface RocketScienceModule : JSObjectionModule -@end - -@implementation RocketScienceModule +@implementation NewCarModule -(void)configure { - [self bindClass:[RocketEngine class] toClass:[Engine class]]; + [self bindClass:[UnregisteredCar class] toClass:[Car class]]; } @end -@interface ExperimentalCar : NSObject -@property (nonatomic, retain) Engine *engine; +@interface CarOwner : NSObject +@property (nonatomic, strong) Car *car; @end -@implementation ExperimentalCar -objection_requires(@"engine") +@implementation CarOwner +objection_requires(@"car") @end QuickSpecBegin(InjectImplementsProtocolSpecs) __block JSObjectionInjector *injector = nil; -__block ExperimentalCar *carWithRocketEngine = nil; +__block CarOwner *carDriver = nil; beforeEach(^{ - injector = [JSObjection createInjector:[RocketScienceModule new]]; - carWithRocketEngine = [ExperimentalCar new]; + injector = [JSObjection createInjector:[NewCarModule new]]; + carDriver = [CarOwner new]; }); -it(@"injects property engine that implements Blinkable and UnregisteredProtocol protocols ", ^{ - [injector injectDependencies:carWithRocketEngine]; - assertThat(carWithRocketEngine.engine, is(instanceOf([RocketEngine class]))); +it(@"injects property car that implements UnregisteredProtocol", ^{ + [injector injectDependencies:carDriver]; + assertThat(carDriver.car, is(instanceOf([UnregisteredCar class]))); }); QuickSpecEnd \ No newline at end of file