diff --git a/A-Typhoon.xcodeproj/project.pbxproj b/A-Typhoon.xcodeproj/project.pbxproj index 8778fb14c..493010064 100644 --- a/A-Typhoon.xcodeproj/project.pbxproj +++ b/A-Typhoon.xcodeproj/project.pbxproj @@ -758,12 +758,12 @@ BA798CB709B2750980CFFC62 /* TyphoonComponentFactory+InstanceBuilder.h */, BA798518E11F23A1021A6528 /* TyphoonComponentFactory+InstanceBuilder.m */, BA79899ED482BE5A9D176ED6 /* TyphoonComponentFactory+TyphoonDefinitionRegisterer.h */, - BA798FC918C219BF4F199085 /* TyphoonCallStack.h */, BA79897F17DB88BEA7549100 /* TyphoonCallStack.m */, - BA798AA10B9FA9B3D53146C0 /* TyphoonStackElement.h */, + BA798FC918C219BF4F199085 /* TyphoonCallStack.h */, BA7981BE9FC036F9CB30E1F9 /* TyphoonStackElement.m */, - BA7988CB0A04D544852FC120 /* TyphoonParentReferenceHydratingPostProcessor.h */, + BA798AA10B9FA9B3D53146C0 /* TyphoonStackElement.h */, BA798E9C35F725B504C22F74 /* TyphoonParentReferenceHydratingPostProcessor.m */, + BA7988CB0A04D544852FC120 /* TyphoonParentReferenceHydratingPostProcessor.h */, ); path = Internal; sourceTree = ""; diff --git a/Source/Definition/Initializer/Internal/TyphoonInitializer+InstanceBuilder.h b/Source/Definition/Initializer/Internal/TyphoonInitializer+InstanceBuilder.h index 3e0d9d6c8..853d087bf 100644 --- a/Source/Definition/Initializer/Internal/TyphoonInitializer+InstanceBuilder.h +++ b/Source/Definition/Initializer/Internal/TyphoonInitializer+InstanceBuilder.h @@ -21,8 +21,6 @@ - (NSInvocation*)asInvocationFor:(id)classOrInstance; -- (NSMethodSignature *) methodSignatureForClass:(Class)clazz; - - (void)setComponentDefinition:(TyphoonDefinition*)definition; diff --git a/Source/Definition/Initializer/Internal/TyphoonInitializer+InstanceBuilder.m b/Source/Definition/Initializer/Internal/TyphoonInitializer+InstanceBuilder.m index 9a26c7b26..1c3ccbed4 100644 --- a/Source/Definition/Initializer/Internal/TyphoonInitializer+InstanceBuilder.m +++ b/Source/Definition/Initializer/Internal/TyphoonInitializer+InstanceBuilder.m @@ -55,24 +55,10 @@ - (NSInvocation*)asInvocationFor:(id)classOrInstance { invocation = [NSInvocation invocationWithMethodSignature:[[classOrInstance class] instanceMethodSignatureForSelector:_selector]]; } - [invocation setTarget:classOrInstance]; [invocation setSelector:_selector]; return invocation; } -- (NSMethodSignature *) methodSignatureForClass:(Class)clazz -{ - NSMethodSignature *signature = nil; - - if (self.isClassMethod) { - signature = [clazz methodSignatureForSelector:_selector]; - } else { - signature = [clazz instanceMethodSignatureForSelector:_selector]; - } - - return signature; -} - - (void)setComponentDefinition:(TyphoonDefinition*)definition { diff --git a/Source/Definition/Initializer/TyphoonParameterInjectedWithObjectInstance.h b/Source/Definition/Initializer/TyphoonParameterInjectedWithObjectInstance.h index 68d437c5c..8c5e99fc2 100644 --- a/Source/Definition/Initializer/TyphoonParameterInjectedWithObjectInstance.h +++ b/Source/Definition/Initializer/TyphoonParameterInjectedWithObjectInstance.h @@ -23,6 +23,6 @@ - (id)initWithParameterIndex:(NSUInteger)index value:(id)value; -- (BOOL) isPrimitiveParameterForClass:(Class)aClass isClassMethod:(BOOL)isClassMethod; +- (BOOL) isPrimitiveParameterFor:(id)classOrInstance; @end diff --git a/Source/Definition/Initializer/TyphoonParameterInjectedWithObjectInstance.m b/Source/Definition/Initializer/TyphoonParameterInjectedWithObjectInstance.m index e98796950..876031abc 100644 --- a/Source/Definition/Initializer/TyphoonParameterInjectedWithObjectInstance.m +++ b/Source/Definition/Initializer/TyphoonParameterInjectedWithObjectInstance.m @@ -42,9 +42,13 @@ - (void)setInitializer:(TyphoonInitializer*)initializer /* ====================================================================================================================================== */ #pragma mark - Interface Methods -- (BOOL) isPrimitiveParameterForClass:(Class)aClass isClassMethod:(BOOL)isClassMethod -{ - NSArray* typeCodes = [TyphoonIntrospectionUtils typeCodesForSelector:_initializer.selector ofClass:aClass isClassMethod:isClassMethod]; +- (BOOL) isPrimitiveParameterFor:(id)classOrInstance +{ + BOOL isClass = class_isMetaClass(object_getClass(classOrInstance)); + + Class class = isClass ? classOrInstance : [classOrInstance class]; + + NSArray* typeCodes = [TyphoonIntrospectionUtils typeCodesForSelector:_initializer.selector ofClass:class isClassMethod:isClass]; return ![[typeCodes objectAtIndex:_index] isEqualToString:@"@"]; } diff --git a/Source/Definition/Initializer/TyphoonParameterInjectedWithStringRepresentation.h b/Source/Definition/Initializer/TyphoonParameterInjectedWithStringRepresentation.h index a07465ac6..0d9260fee 100644 --- a/Source/Definition/Initializer/TyphoonParameterInjectedWithStringRepresentation.h +++ b/Source/Definition/Initializer/TyphoonParameterInjectedWithStringRepresentation.h @@ -43,7 +43,4 @@ */ - (TyphoonTypeDescriptor*)resolveTypeWith:(id)classOrInstance; -- (TyphoonTypeDescriptor*)resolveTypeWith:(Class)clazz isClassMethod:(BOOL)isClassMethod; - - @end diff --git a/Source/Definition/Initializer/TyphoonParameterInjectedWithStringRepresentation.m b/Source/Definition/Initializer/TyphoonParameterInjectedWithStringRepresentation.m index 1351539f0..04c5be320 100644 --- a/Source/Definition/Initializer/TyphoonParameterInjectedWithStringRepresentation.m +++ b/Source/Definition/Initializer/TyphoonParameterInjectedWithStringRepresentation.m @@ -69,24 +69,6 @@ - (TyphoonTypeDescriptor*)resolveTypeWith:(id)classOrInstance } } -- (TyphoonTypeDescriptor*)resolveTypeWith:(Class)clazz isClassMethod:(BOOL)isClassMethod -{ - if (_requiredType) - { - return [TyphoonTypeDescriptor descriptorWithClassOrProtocol:_requiredType]; - } - else - { - NSArray* typeCodes = [TyphoonIntrospectionUtils typeCodesForSelector:_initializer.selector ofClass:clazz isClassMethod:isClassMethod]; - - if ([[typeCodes objectAtIndex:_index] isEqualToString:@"@"]) - { - [NSException raise:NSInvalidArgumentException - format:@"Unless the type is primitive (int, BOOL, etc), initializer injection requires the required class to be specified. Eg: "]; - } - return [TyphoonTypeDescriptor descriptorWithTypeCode:[typeCodes objectAtIndex:_index]]; - } -} /* ====================================================================================================================================== */ #pragma mark - Protocol Methods diff --git a/Source/Factory/Internal/TyphoonComponentFactory+InstanceBuilder.h b/Source/Factory/Internal/TyphoonComponentFactory+InstanceBuilder.h index f5c70d93c..2891910ed 100644 --- a/Source/Factory/Internal/TyphoonComponentFactory+InstanceBuilder.h +++ b/Source/Factory/Internal/TyphoonComponentFactory+InstanceBuilder.h @@ -21,9 +21,9 @@ */ @interface TyphoonComponentFactory (InstanceBuilder) -- (id)buildInstanceWithDefinition:(TyphoonDefinition*)definition NS_RETURNS_RETAINED; +- (id)buildInstanceWithDefinition:(TyphoonDefinition*)definition; -- (id)buildSharedInstanceForDefinition:(TyphoonDefinition*)definition NS_RETURNS_RETAINED; +- (id)buildSharedInstanceForDefinition:(TyphoonDefinition*)definition; - (void)injectPropertyDependenciesOn:(__autoreleasing id)instance withDefinition:(TyphoonDefinition*)definition; diff --git a/Source/Factory/Internal/TyphoonComponentFactory+InstanceBuilder.m b/Source/Factory/Internal/TyphoonComponentFactory+InstanceBuilder.m index e016890cf..76ae493b6 100644 --- a/Source/Factory/Internal/TyphoonComponentFactory+InstanceBuilder.m +++ b/Source/Factory/Internal/TyphoonComponentFactory+InstanceBuilder.m @@ -45,67 +45,64 @@ #import "TyphoonStackElement.h" #import "NSObject+PropertyInjection.h" -#import - #define AssertTypeDescriptionForPropertyOnInstance(type, property, instance) if (!type)[NSException raise:@"NSUnknownKeyException" \ format:@"Tried to inject property '%@' on object of type '%@', but the instance has no setter for this property.",property.name, [instance class]] +#define RaiseInitCircualrException(definitionKey) [NSException raise:@"CircularInitializerDependence" format:@"The object for key %@ is currently initializing, but was specified as init dependency in another object",definitionKey] + @implementation TyphoonComponentFactory (InstanceBuilder) /* ====================================================================================================================================== */ #pragma mark - Initialization & Destruction -- (id) buildInstanceWithDefinition:(TyphoonDefinition*)definition +- (id)buildInstanceWithDefinition:(TyphoonDefinition*)definition { - id instance = [self newInstanceWithDefinition:definition]; - [_stack push:[TyphoonStackElement itemWithKey:definition.key instance:instance]]; + TyphoonStackElement *stackElement = [TyphoonStackElement itemWithKey:definition.key]; + [_stack push:stackElement]; + + id instance = [self newInstanceWithDefinition:definition]; + + [stackElement takeInstance:instance]; + [self injectPropertyDependenciesOn:instance withDefinition:definition]; + instance = [self postProcessInstance:instance]; [_stack pop]; - + return instance; } - (id) newInstanceWithDefinition:(TyphoonDefinition*)definition { - id instance = nil; + id initTarget = nil; if (definition.factoryReference) { // misleading - this is not the instance. this is an instance of a separate class that will create the instance of the class we care // about. - instance = [self componentForKey:definition.factoryReference]; // clears currently resolving. + initTarget = [self componentForKey:definition.factoryReference]; // clears currently resolving. } - else - { - /* Sending init later after alloc is dangerous and was reason of few bugs, - * It was a reason to refactor */ - - id target = definition.initializer.isClassMethod ? definition.type : [definition.type alloc]; - - void *initializedObjectPointer = NULL; - - NSInvocation *invocation = [self invocationToInitDefinition:definition]; - [invocation invokeWithTarget:target]; - [invocation getReturnValue:&initializedObjectPointer]; - - instance = (__bridge id) initializedObjectPointer; + else if (definition.initializer.isClassMethod) { + initTarget = definition.type; } + else { + initTarget = [definition.type alloc]; + } + + void *resultPointer; + NSInvocation *invocation = [self invocationToInit:initTarget withDefinition:definition]; + [invocation invokeWithTarget:initTarget]; + [invocation getReturnValue:&resultPointer]; + + id instance = (__bridge id) resultPointer; + + /* Check this case again. I think this problem might be fixed now */ [self handleSpecialCaseForNSManagedObjectModel:instance]; return instance; } -//- (id)injectInstance:(id)instance withDefinition:(TyphoonDefinition*)definition UNAVAILABLE_ATTRIBUTE -//{ -// instance = [self initializerInjectionOn:instance withDefinition:definition]; -// [_stack push:[TyphoonStackElement itemWithKey:definition.key instance:instance]]; -// [self injectPropertyDependenciesOn:instance withDefinition:definition]; -// [_stack pop]; -// return instance; -//} - - (id)postProcessInstance:(id)instance { if (![instance conformsToProtocol:@protocol(TyphoonComponentPostProcessor)]) @@ -118,31 +115,23 @@ - (id)postProcessInstance:(id)instance return instance; } -//- (id)initializerInjectionOn:(id)instance withDefinition:(TyphoonDefinition*)definition UNAVAILABLE_ATTRIBUTE -//{ -// if (definition.initializer) -// { -// if (definition.initializer.isClassMethod == NO) -// { -// instance = [self invokeInitializer:definition.initializer on:instance]; -// } -// else -// { -// // initializer was already invoked in allocateInstance:withDefinition: -// } -// } -// else if (definition.initializer == nil) -// { -// instance = [self invokeDefaultInitializerOn:instance]; -// } -// -// return instance; -//} +- (NSInvocation *) invocationToInit:(id)instanceOrClass withDefinition:(TyphoonDefinition *)definition +{ + NSInvocation *invocation = nil; + if (definition.initializer) { + invocation = [self invocationToInit:instanceOrClass withInitializer:definition.initializer]; + } else { + invocation = [self defaultInvocationToInit:instanceOrClass]; + } + return invocation; +} -- (id)invokeDefaultInitializerOn:(id)instance +- (NSInvocation *) defaultInvocationToInit:(id)instance { - id initializedInstance = objc_msgSend(instance, @selector(init)); - return initializedInstance; + NSMethodSignature *methodSignature = [instance methodSignatureForSelector:@selector(init)]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + [invocation setSelector:@selector(init)]; + return invocation; } - (void)injectAssemblyOnInstanceIfTyphoonAware:(id)instance; @@ -162,6 +151,11 @@ - (id)buildSharedInstanceForDefinition:(TyphoonDefinition*)definition { if ([self alreadyResolvingKey:definition.key]) { + TyphoonStackElement *stackElement = [_stack peekForKey:definition.key]; + if ([stackElement isInitializingInstance]) { + RaiseInitCircualrException(definition.key); + } + return [_stack peekForKey:definition.key].instance; } return [self buildInstanceWithDefinition:definition]; @@ -176,7 +170,7 @@ - (BOOL)alreadyResolvingKey:(NSString*)key /* ====================================================================================================================================== */ #pragma mark - Property Injection -- (void)injectPropertyDependenciesOn:(__autoreleasing id)instance withDefinition:(TyphoonDefinition*)definition +- (void)injectPropertyDependenciesOn:(id)instance withDefinition:(TyphoonDefinition*)definition { [self doBeforePropertyInjectionOn:instance withDefinition:definition]; @@ -285,7 +279,7 @@ - (BOOL)propertyIsCircular:(TyphoonAbstractInjectedProperty*)property onInstance return [[instance circularDependentProperties] objectForKey:property.name] != nil; } -- (void)injectCircularDependenciesOn:(__autoreleasing id )instance +- (void)injectCircularDependenciesOn:(id )instance { NSMutableDictionary* circularDependentProperties = [instance circularDependentProperties]; for (NSString* propertyName in [circularDependentProperties allKeys]) @@ -294,8 +288,10 @@ - (void)injectCircularDependenciesOn:(__autoreleasing id )instance w /* ====================================================================================================================================== */ #pragma mark - Private Methods -- (NSInvocation *) invocationToInitDefinition:(TyphoonDefinition *)definition +- (NSInvocation *) invocationToInit:(id)instanceOrClass withInitializer:(TyphoonInitializer*)initializer { - NSInvocation *invocation = nil; - - if (definition.initializer) { - invocation = [self invocationForInitializer:definition.initializer withClass:definition.type]; - } else { - invocation = [self defaultInitInvocation]; - } - - return invocation; -} - -- (NSInvocation *) defaultInitInvocation -{ - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSObject instanceMethodSignatureForSelector:@selector(init)]]; - [invocation setSelector:@selector(init)]; - return invocation; -} - -- (NSInvocation *) invocationForInitializer:(TyphoonInitializer *)initializer withClass:(Class)class -{ - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[initializer methodSignatureForClass:class]]; - - [self configureInvocation:invocation forClass:class withParametersFromInitializer:initializer]; - - [invocation setSelector:initializer.selector]; - - return invocation; -} + NSInvocation* invocation = [initializer asInvocationFor:instanceOrClass]; -- (void) configureInvocation:(NSInvocation *)invocation forClass:(Class)clazz withParametersFromInitializer:(TyphoonInitializer *)initializer -{ - for (id parameter in [initializer injectedParameters]) + NSArray* injectedParameters = [initializer injectedParameters]; + for (id parameter in injectedParameters) { if (parameter.type == TyphoonParameterInjectionTypeReference) { TyphoonParameterInjectedByReference* byReference = (TyphoonParameterInjectedByReference*)parameter; + + if ([[_stack peekForKey:byReference.reference] isInitializingInstance]) { + RaiseInitCircualrException(byReference.reference); + } + id reference = [self componentForKey:byReference.reference]; [invocation setArgument:&reference atIndex:parameter.index + 2]; } else if (parameter.type == TyphoonParameterInjectionTypeStringRepresentation) { TyphoonParameterInjectedWithStringRepresentation* byValue = (TyphoonParameterInjectedWithStringRepresentation*)parameter; - TyphoonTypeDescriptor *type = [byValue resolveTypeWith:clazz isClassMethod:initializer.isClassMethod]; - - [self setArgumentFor:invocation index:byValue.index + 2 textValue:byValue.textValue requiredType:type]; + [self setArgumentFor:invocation index:byValue.index + 2 textValue:byValue.textValue + requiredType:[byValue resolveTypeWith:instanceOrClass]]; } else if (parameter.type == TyphoonParameterInjectionTypeObjectInstance) { TyphoonParameterInjectedWithObjectInstance* byInstance = (TyphoonParameterInjectedWithObjectInstance*)parameter; id value = byInstance.value; BOOL isValuesIsWrapper = [value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSValue class]]; - - if (isValuesIsWrapper && [byInstance isPrimitiveParameterForClass:clazz isClassMethod:initializer.isClassMethod]) { + + if (isValuesIsWrapper && [byInstance isPrimitiveParameterFor:instanceOrClass]) { [self setPrimitiveArgumentForInvocation:invocation index:parameter.index + 2 fromValue:value]; } else { [invocation setArgument:&value atIndex:parameter.index + 2]; @@ -385,51 +357,9 @@ - (void) configureInvocation:(NSInvocation *)invocation forClass:(Class)clazz wi [invocation setArgument:&collection atIndex:parameter.index + 2]; } } -} -//- (id)invokeInitializer:(TyphoonInitializer*)initializer on:(id)instanceOrClass UNAVAILABLE_ATTRIBUTE -//{ -// NSInvocation* invocation = [initializer asInvocationFor:instanceOrClass]; -// -// NSArray* injectedParameters = [initializer injectedParameters]; -// for (id parameter in injectedParameters) -// { -// if (parameter.type == TyphoonParameterInjectionTypeReference) -// { -// TyphoonParameterInjectedByReference* byReference = (TyphoonParameterInjectedByReference*)parameter; -// id reference = [self componentForKey:byReference.reference]; -// [invocation setArgument:&reference atIndex:parameter.index + 2]; -// } -// else if (parameter.type == TyphoonParameterInjectionTypeStringRepresentation) -// { -// TyphoonParameterInjectedWithStringRepresentation* byValue = (TyphoonParameterInjectedWithStringRepresentation*)parameter; -// [self setArgumentFor:invocation index:byValue.index + 2 textValue:byValue.textValue -// requiredType:[byValue resolveTypeWith:instanceOrClass]]; -// } -// else if (parameter.type == TyphoonParameterInjectionTypeObjectInstance) -// { -// TyphoonParameterInjectedWithObjectInstance* byInstance = (TyphoonParameterInjectedWithObjectInstance*)parameter; -// id value = byInstance.value; -// BOOL isValuesIsWrapper = [value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSValue class]]; -// -// if (isValuesIsWrapper && [byInstance isPrimitiveParameterFor:instanceOrClass]) { -// [self setPrimitiveArgumentForInvocation:invocation index:parameter.index + 2 fromValue:value]; -// } else { -// [invocation setArgument:&value atIndex:parameter.index + 2]; -// } -// } -// else if (parameter.type == TyphoonParameterInjectionTypeAsCollection) -// { -// TyphoonParameterInjectedAsCollection* asCollection = (TyphoonParameterInjectedAsCollection*)parameter; -// id collection = [self buildCollectionWithValues:asCollection.values requiredType:asCollection.collectionType]; -// [invocation setArgument:&collection atIndex:parameter.index + 2]; -// } -// } -// [invocation invoke]; -// __autoreleasing id returnValue = nil; -// [invocation getReturnValue:&returnValue]; -// return returnValue; -//} + return invocation; +} /* ====================================================================================================================================== */ diff --git a/Source/Factory/Internal/TyphoonStackElement.h b/Source/Factory/Internal/TyphoonStackElement.h index 8229c5f57..a16b7b0a6 100644 --- a/Source/Factory/Internal/TyphoonStackElement.h +++ b/Source/Factory/Internal/TyphoonStackElement.h @@ -14,16 +14,21 @@ @class TyphoonDefinition; +typedef void(^TyphoonInstanceCompleteBlock)(id instance); + @interface TyphoonStackElement : NSObject @property(nonatomic, strong, readonly) NSString* key; @property(nonatomic, strong, readonly) id instance; - -+ (instancetype)itemWithKey:(NSString*)key instance:(id)instance; ++ (instancetype)itemWithKey:(NSString*)key; - (NSString*)description; +- (BOOL) isInitializingInstance; +- (void) addInstanceCompleteBlock:(TyphoonInstanceCompleteBlock)completeBlock; + +- (void) takeInstance:(id)instance; @end \ No newline at end of file diff --git a/Source/Factory/Internal/TyphoonStackElement.m b/Source/Factory/Internal/TyphoonStackElement.m index 10fef375e..29f264078 100644 --- a/Source/Factory/Internal/TyphoonStackElement.m +++ b/Source/Factory/Internal/TyphoonStackElement.m @@ -14,30 +14,59 @@ #import "TyphoonStackElement.h" -@implementation TyphoonStackElement +@implementation TyphoonStackElement { + NSMutableSet *completeBlocks; +} -+ (instancetype)itemWithKey:(NSString*)key instance:(id)instance; ++ (instancetype)itemWithKey:(NSString*)key { - return [[self alloc] initWithKey:key instance:instance]; + return [[self alloc] initWithKey:key]; } -- (instancetype)initWithKey:(NSString*)key instance:(id)instance +- (instancetype)initWithKey:(NSString*)key { self = [super init]; if (self) { _key = key; - _instance = instance; + completeBlocks = [NSMutableSet new]; } return self; } +- (BOOL) isInitializingInstance +{ + return _instance == nil; +} + +- (void) addInstanceCompleteBlock:(TyphoonInstanceCompleteBlock)completeBlock +{ + NSParameterAssert(completeBlock); + + if ([self isInitializingInstance]) { + [completeBlocks addObject:completeBlock]; + } else { + completeBlock(_instance); + } + +} + +- (void) takeInstance:(id)instance +{ + _instance = instance; + + for (TyphoonInstanceCompleteBlock completeBlock in completeBlocks) { + completeBlock(instance); + } + [completeBlocks removeAllObjects]; +} + - (NSString*)description { NSMutableString* description = [NSMutableString stringWithFormat:@"<%@: ", NSStringFromClass([self class])]; [description appendFormat:@"self.key=%@", self.key]; - [description appendFormat:@", self.instance=%@", self.instance]; + [description appendFormat:@", completeBlocksCount=%d", [completeBlocks count]]; [description appendString:@">"]; return description; } diff --git a/Source/Factory/TyphoonComponentFactory.m b/Source/Factory/TyphoonComponentFactory.m index 9b19fd020..928012cd0 100644 --- a/Source/Factory/TyphoonComponentFactory.m +++ b/Source/Factory/TyphoonComponentFactory.m @@ -294,14 +294,14 @@ - (id)instanceForDefinition:(TyphoonDefinition*)definition fromPool:(id)pool NS_RETURNS_RETAINED +- (id) sharedObjectGraphInstanceForDefinition:(TyphoonDefinition*)definition fromPool:(id)pool { return [self instanceForDefinition:definition fromPool:pool buildBlock:^id(TyphoonDefinition *definition) { return [self buildSharedInstanceForDefinition:definition]; }]; } -- (id) sharedInstanceForDefinition:(TyphoonDefinition*)definition fromPool:(id)pool NS_RETURNS_RETAINED +- (id) sharedInstanceForDefinition:(TyphoonDefinition*)definition fromPool:(id)pool { return [self instanceForDefinition:definition fromPool:pool buildBlock:^id(TyphoonDefinition *definition) { return [self buildInstanceWithDefinition:definition]; diff --git a/Source/Utils/NSObject+PropertyInjection.m b/Source/Utils/NSObject+PropertyInjection.m index 35123d25f..a2e62dacc 100644 --- a/Source/Utils/NSObject+PropertyInjection.m +++ b/Source/Utils/NSObject+PropertyInjection.m @@ -36,6 +36,9 @@ - (void) injectValue:(id)value forPropertyName:(NSString *)propertyName withType [self injectValue:value asPointerForPropertyName:propertyName]; } else { [self setValue:value forKey:propertyName]; +#if !__has_feature(objc_arc) +#error Arc feature required! +#endif } } diff --git a/Tests/Factory/Block/TestAssemblies/CircularDependenciesAssembly.m b/Tests/Factory/Block/TestAssemblies/CircularDependenciesAssembly.m index 136aebbe0..3248bdf6f 100644 --- a/Tests/Factory/Block/TestAssemblies/CircularDependenciesAssembly.m +++ b/Tests/Factory/Block/TestAssemblies/CircularDependenciesAssembly.m @@ -143,4 +143,28 @@ - (id)croPrototypeB }]; } +// Incorrect circular dependency + +- (id)incorrectPrototypeB +{ + return [TyphoonDefinition withClass:[CROPrototypeB class] initialization:^(TyphoonInitializer *initializer) { + initializer.selector = @selector(initWithCROPrototypeA:); + [initializer injectWithDefinition:[self incorrectPrototypeA]]; + } properties:^(TyphoonDefinition *definition) { + definition.key = @"incorrectPrototypeB"; + definition.scope = TyphoonScopePrototype; + }]; +} + +- (id)incorrectPrototypeA +{ + return [TyphoonDefinition withClass:[CROPrototypeA class] initialization:^(TyphoonInitializer* initializer){ + initializer.selector = @selector(initWithCROPrototypeB:); + [initializer injectWithDefinition:[self incorrectPrototypeB]]; + } properties:^(TyphoonDefinition *definition) { + definition.key = @"incorrectPrototypeA"; + definition.scope = TyphoonScopePrototype; + }]; +} + @end \ No newline at end of file diff --git a/Tests/Factory/Block/TestAssemblies/ObjectGraphAssembly.m b/Tests/Factory/Block/TestAssemblies/ObjectGraphAssembly.m index 248da730b..81c8a0f81 100644 --- a/Tests/Factory/Block/TestAssemblies/ObjectGraphAssembly.m +++ b/Tests/Factory/Block/TestAssemblies/ObjectGraphAssembly.m @@ -50,6 +50,7 @@ - (id)prototypeKnight { [definition injectProperty:@selector(homeFort) withDefinition:[self prototypeFort]]; [definition injectProperty:@selector(quest) withDefinition:[self prototypeQuest]]; + [definition setScope:TyphoonScopePrototype]; }]; } diff --git a/Tests/Factory/Shared/TyphoonSharedComponentFactoryTests.m b/Tests/Factory/Shared/TyphoonSharedComponentFactoryTests.m index 382a07473..fbb3572e0 100644 --- a/Tests/Factory/Shared/TyphoonSharedComponentFactoryTests.m +++ b/Tests/Factory/Shared/TyphoonSharedComponentFactoryTests.m @@ -277,7 +277,22 @@ - (void)test_currently_resolving_references_dictionary_is_not_overwritten_when_i { CROSingletonA* singletonA = [_circularDependenciesFactory componentForType:[CROSingletonA class]]; assertThat(singletonA.prototypeB, isNot(nilValue())); + assertThat(singletonA.prototypeA, isNot(nilValue())); } +- (void)test_exception_when_initializer_dependency_chain +{ + @try { + [_circularDependenciesFactory componentForKey:@"incorrectPrototypeB"]; + STFail(@"Should have thrown exception"); + } + @catch (NSException *exception) { + assertThat([exception name], equalTo(@"CircularInitializerDependence")); + } + + + +} + @end diff --git a/Tests/Factory/TyphoonComponentFactoryTests.m b/Tests/Factory/TyphoonComponentFactoryTests.m index 70c9fe2a6..56b0d1f22 100644 --- a/Tests/Factory/TyphoonComponentFactoryTests.m +++ b/Tests/Factory/TyphoonComponentFactoryTests.m @@ -363,38 +363,39 @@ - (void)test_load_weakSingleton { TyphoonComponentFactory *localFactory = [[TyphoonComponentFactory alloc] init]; NSString *key = @"WeakSingleton"; - [localFactory register:[TyphoonDefinition withClass:[NSMutableString class] initialization:^(TyphoonInitializer *initializer) { - [initializer setSelector:@selector(new)]; - } properties:^(TyphoonDefinition* definition) { - [definition setKey:key]; - [definition setScope:TyphoonScopeWeakSingleton]; - }]]; - + [localFactory register:[TyphoonDefinition withClass:[NSMutableIndexSet class] properties:^(TyphoonDefinition* definition) + { + [definition setKey:key]; + [definition setScope:TyphoonScopeWeakSingleton]; + }]]; [localFactory load]; - NSMutableString *requestedString, *requestedString2; + NSMutableIndexSet *indexSet1, *indexSet2; + __weak NSMutableIndexSet *weakRef; + __unsafe_unretained NSMutableIndexSet *unsafeRef; @autoreleasepool { - requestedString = [localFactory componentForKey:key]; - requestedString2 = [localFactory componentForKey:key]; - assertThat(requestedString, equalTo(requestedString2)); - [requestedString setString:@"Text!"]; + indexSet1 = [localFactory componentForKey:key]; + indexSet2 = [localFactory componentForKey:key]; + assertThat(indexSet1, equalTo(indexSet2)); + } + [indexSet1 addIndex:5]; - __weak NSMutableString *weakString = requestedString2; - __unsafe_unretained NSMutableString *unsafeString = requestedString2; + weakRef = indexSet2; + unsafeRef = indexSet2; - requestedString = nil; - requestedString2 = nil; + indexSet1 = nil; + indexSet2 = nil; + + assertThat(weakRef, equalTo(nil)); - assertThat(weakString, equalTo(nil)); - @autoreleasepool { - requestedString = [localFactory componentForKey:key]; + indexSet1 = [localFactory componentForKey:key]; } - - STAssertTrue(unsafeString != requestedString, @"%p == %p",unsafeString, requestedString); - + + STAssertTrue(unsafeRef != indexSet1, @"%p == %p",unsafeRef, indexSet1); + STAssertTrue([indexSet1 firstIndex] != 5, nil); } /* ====================================================================================================================================== */ diff --git a/Tests/Model/CROPrototypeB.h b/Tests/Model/CROPrototypeB.h index 412a8aae5..4779a96b7 100644 --- a/Tests/Model/CROPrototypeB.h +++ b/Tests/Model/CROPrototypeB.h @@ -9,11 +9,15 @@ #import @class CROSingletonA; +@class CROPrototypeA; @interface CROPrototypeB : NSObject @property(nonatomic, strong, readonly) CROSingletonA* singletonA; +@property(nonatomic, strong, readonly) CROPrototypeA* prototypeA; - (id)initWithCROSingletonA:(CROSingletonA*)singletonA; +- (id)initWithCROPrototypeA:(CROPrototypeA *)prototypeA; + @end diff --git a/Tests/Model/CROPrototypeB.m b/Tests/Model/CROPrototypeB.m index 7b14ff19d..dbaada290 100644 --- a/Tests/Model/CROPrototypeB.m +++ b/Tests/Model/CROPrototypeB.m @@ -20,4 +20,13 @@ - (id)initWithCROSingletonA:(CROSingletonA*)singletonA return self; } +- (id)initWithCROPrototypeA:(CROPrototypeA *)prototypeA +{ + self = [super init]; + if (self) { + _prototypeA = prototypeA; + } + return self; +} + @end diff --git a/Tests/Model/CROSingletonA.h b/Tests/Model/CROSingletonA.h index c508f961d..56fa765e4 100644 --- a/Tests/Model/CROSingletonA.h +++ b/Tests/Model/CROSingletonA.h @@ -17,4 +17,6 @@ @property(nonatomic, strong) CROPrototypeA* prototypeA; @property(nonatomic, strong) CROPrototypeB* prototypeB; +- (id) initWithPrototypeB:(id)b; + @end diff --git a/Tests/Model/CROSingletonA.m b/Tests/Model/CROSingletonA.m index 14f2e25df..9b17a6a3e 100644 --- a/Tests/Model/CROSingletonA.m +++ b/Tests/Model/CROSingletonA.m @@ -10,4 +10,13 @@ @implementation CROSingletonA +- (id) initWithPrototypeB:(id)b +{ + self = [super init]; + if (self) { + self.prototypeB = b; + } + return self; +} + @end diff --git a/Tests/Resources/CircularDependenciesAssembly.xml b/Tests/Resources/CircularDependenciesAssembly.xml index b22116bcc..5b541a95b 100644 --- a/Tests/Resources/CircularDependenciesAssembly.xml +++ b/Tests/Resources/CircularDependenciesAssembly.xml @@ -52,6 +52,20 @@ + + + + + + + + + + + + + + diff --git a/Tests/Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests.xcscheme b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests.xcscheme index c84efb439..b3a5641cc 100644 --- a/Tests/Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests.xcscheme +++ b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests.xcscheme @@ -1,16 +1,11 @@ - + + buildForTesting = "YES"> - + - - + useCustomWorkingDirectory = "NO"> - - - - - -