Created
October 8, 2012 17:20
-
-
Save nevyn/3853718 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@protocol IThingieProvider <NSObject> | |
-(id)thingie; | |
@end | |
@protocol IDependent <NSObject> | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// main.m | |
// SPDependencyContainer | |
// | |
// Created by Joachim Bengtsson on 2012-10-08. | |
// Copyright (c) 2012 Spotify AB. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "SPDependencyContainer.h" | |
#import "Interfaces.h" | |
#import "SPWeakArray.h" | |
@interface ThingieProvider : NSObject <IThingieProvider> | |
@end | |
@interface MockedThingieProvider : NSObject <IThingieProvider> | |
@end | |
@interface Dependent : NSObject <IDependent> | |
@property(nonatomic,strong) id<IThingieProvider> thingieProvider; | |
- (NSString*)description; | |
@end | |
int main(int argc, const char * argv[]) | |
{ | |
@autoreleasepool { | |
id<IThingieProvider> thingieProvider = [[SPDependencyContainer sharedContainer] instanceForProtocol:@protocol(IThingieProvider) arguments:@[@"Specific thingie"]]; | |
// insert code here... | |
NSLog(@"Creating from protocol: %@", thingieProvider.thingie); | |
id dependent = [[SPDependencyContainer sharedContainer] instanceForProtocol:@protocol(IDependent)]; | |
NSLog(@"Creating through injection: %@", dependent); | |
[[SPDependencyContainer sharedContainer] registerClass:[MockedThingieProvider class] forProtocol:@protocol(IThingieProvider)]; | |
dependent = [[SPDependencyContainer sharedContainer] instanceForProtocol:@protocol(IDependent)]; | |
NSLog(@"Injecting mocked: %@", dependent); | |
id singleton = [ThingieProvider new]; | |
[[SPDependencyContainer sharedContainer] registerSingleton:singleton forProtocol:@protocol(IThingieProvider)]; | |
dependent = [[SPDependencyContainer sharedContainer] instanceForProtocol:@protocol(IDependent)]; | |
NSLog(@"Injecting singleton: %@ into: %@", singleton, dependent); | |
[[SPDependencyContainer sharedContainer] removeSingletonForProtocol:@protocol(IThingieProvider)]; | |
dependent = [[SPDependencyContainer sharedContainer] instanceForProtocol:@protocol(IDependent)]; | |
NSLog(@"Injecting with killed singleton: %@", dependent); | |
} | |
SPMutableWeakArray *weaks = [SPMutableWeakArray new]; | |
{ | |
id thing = [NSObject new]; | |
[weaks addObject:thing]; | |
NSLog(@"Weaks: %@", weaks); | |
} | |
[weaks pruneNilEntries]; | |
NSLog(@"Weaks after scope: %@", weaks); | |
return 0; | |
} | |
@implementation ThingieProvider { | |
id _otherThing; | |
} | |
+ (void)load | |
{ | |
if(self == [ThingieProvider class]) | |
[[SPDependencyContainer sharedContainer] registerClass:self forProtocol:@protocol(IThingieProvider)]; | |
} | |
- (id)initWithArguments:(NSArray*)arguments; | |
{ | |
if(arguments) | |
_otherThing = arguments[0]; | |
return self; | |
} | |
- (id)thingie; | |
{ | |
return _otherThing ?: @"Concrete thingie"; | |
} | |
@end | |
@implementation MockedThingieProvider | |
- (id)thingie; | |
{ | |
return @"Mocked thingie"; | |
} | |
@end | |
@implementation Dependent | |
+ (void)load | |
{ | |
if(self == [Dependent class]) | |
[[SPDependencyContainer sharedContainer] registerClass:self forProtocol:@protocol(IDependent)]; | |
} | |
- (NSString*)description | |
{ | |
return [NSString stringWithFormat:@"<%p %@ %@>", self, self.thingieProvider, self.thingieProvider.thingie]; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SPDependencyContainer.h | |
// SPDependencyContainer | |
// | |
// Created by Joachim Bengtsson on 2012-10-08. | |
// Copyright (c) 2012 Spotify AB. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@interface SPDependencyContainer : NSObject | |
+ (id)sharedContainer; | |
- (void)registerSingleton:(id)singleton forProtocol:(Protocol*)proto; | |
- (void)removeSingletonForProtocol:(Protocol*)protocol; | |
- (void)registerClass:(Class)klass forProtocol:(Protocol*)proto; | |
- (id)instanceForProtocol:(Protocol*)proto; | |
- (id)instanceForProtocol:(Protocol*)proto arguments:(NSArray*)arguments; | |
- (void)injectDependencies:(id)target; | |
@end | |
@protocol SPDependencyCreatable <NSObject> | |
- (id)initWithArguments:(NSArray*)arguments; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SPDependencyContainer.m | |
// SPDependencyContainer | |
// | |
// Created by Joachim Bengtsson on 2012-10-08. | |
// Copyright (c) 2012 Spotify AB. All rights reserved. | |
// | |
#import "SPDependencyContainer.h" | |
#import <objc/Protocol.h> | |
#import "MARTNSObject.h" | |
#import "RTProperty.h" | |
@interface SPDependencyContainer () | |
{ | |
NSMutableDictionary *_singletons; | |
NSMutableDictionary *_classes; | |
} | |
@end | |
@implementation SPDependencyContainer | |
+ (id)sharedContainer | |
{ | |
static SPDependencyContainer *singleton; | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
singleton = [SPDependencyContainer new]; | |
}); | |
return singleton; | |
} | |
- (id)init | |
{ | |
if (!(self = [super init])) | |
return nil; | |
_singletons = [NSMutableDictionary new]; | |
_classes = [NSMutableDictionary new]; | |
return self; | |
} | |
#pragma Proto > Instance map | |
- (void)registerSingleton:(id)singleton forProtocol:(Protocol*)proto | |
{ | |
NSAssert([singleton conformsToProtocol:proto], @"%@ doesn't conform to %@", [singleton class], proto); | |
[_singletons setObject:singleton forKey:[self keyForProtocol:proto]]; | |
} | |
- (void)removeSingletonForProtocol:(Protocol*)protocol | |
{ | |
[_singletons removeObjectForKey:[self keyForProtocol:protocol]]; | |
} | |
- (void)registerClass:(Class)klass forProtocol:(Protocol*)proto | |
{ | |
NSAssert([klass conformsToProtocol:proto], @"%@ doesn't actually conform to %@", klass, proto); | |
[_classes setObject:klass forKey:[self keyForProtocol:proto]]; | |
} | |
- (id)instanceForProtocol:(Protocol*)proto | |
{ | |
return [self instanceForProtocol:proto arguments:nil]; | |
} | |
- (id)instanceForProtocol:(Protocol*)proto arguments:(NSArray*)arguments | |
{ | |
id key = [self keyForProtocol:proto]; | |
id instance = [_singletons objectForKey:key]; | |
if (instance) | |
return instance; | |
Class klass = [_classes objectForKey:key]; | |
if (!klass) | |
return nil; | |
if ([klass instancesRespondToSelector:@selector(initWithArguments:)]) { | |
instance = [[klass alloc] initWithArguments:arguments]; | |
} else { | |
instance = [[klass alloc] init]; | |
} | |
if (instance) | |
[self injectDependencies:instance]; | |
return instance; | |
} | |
- (id)keyForProtocol:(Protocol*)proto | |
{ | |
NSValue *key = [NSValue valueWithPointer:(__bridge const void *)(proto)]; | |
return key; | |
} | |
#pragma mark Injection | |
- (void)injectDependencies:(id)target | |
{ | |
for(RTProperty *prop in [[target class] rt_properties]) | |
[self injectProperty:prop on:target]; | |
} | |
- (void)injectProperty:(RTProperty*)prop on:(id)target | |
{ | |
NSString *type = [prop typeEncoding]; | |
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"@\"<(\\w+)>\"" options:0 error:NULL]; | |
NSTextCheckingResult *checking = [regex firstMatchInString:type options:0 range:NSMakeRange(0, type.length)]; | |
if (checking.numberOfRanges < 2) | |
return; | |
NSString *protocolName = [type substringWithRange:[checking rangeAtIndex:1]]; | |
Protocol *proto = NSProtocolFromString(protocolName); | |
if (!proto) | |
return; | |
id instance = [self instanceForProtocol:proto arguments:nil]; | |
if (!instance) { | |
NSLog(@"WARNING: Failed to inject a %@ into %@.%@", protocolName, target, prop.name); | |
return; | |
} | |
[target setValue:instance forKey:prop.name]; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
/** Holds weak references. Note that objectAtIndex may return nil! */ | |
@interface SPMutableWeakArray : NSMutableArray | |
- (id)init; | |
- (void)pruneNilEntries; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "SPWeakArray.h" | |
@interface SPWeakHolder : NSObject | |
@property(nonatomic, weak) id object; | |
@end | |
@implementation SPMutableWeakArray { | |
NSMutableArray *_weakHolders; | |
} | |
- (id)init | |
{ | |
if (!(self = [super init])) | |
return nil; | |
_weakHolders = [NSMutableArray new]; | |
return self; | |
} | |
- (NSUInteger)count | |
{ | |
return [_weakHolders count]; | |
} | |
- (id)objectAtIndex:(NSUInteger)index | |
{ | |
return [[_weakHolders objectAtIndex:index] object]; | |
} | |
- (void)addObject:(id)anObject | |
{ | |
SPWeakHolder *holder = [SPWeakHolder new]; | |
holder.object = anObject; | |
[_weakHolders addObject:holder]; | |
} | |
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index | |
{ | |
SPWeakHolder *holder = [SPWeakHolder new]; | |
holder.object = anObject; | |
[_weakHolders insertObject:holder atIndex:index]; | |
} | |
- (void)removeLastObject | |
{ | |
[_weakHolders removeLastObject]; | |
} | |
- (void)removeObjectAtIndex:(NSUInteger)index | |
{ | |
[_weakHolders removeObjectAtIndex:index]; | |
} | |
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject | |
{ | |
SPWeakHolder *holder = [SPWeakHolder new]; | |
holder.object = anObject; | |
[_weakHolders replaceObjectAtIndex:index withObject:anObject]; | |
} | |
- (void)pruneNilEntries | |
{ | |
NSIndexSet *toRemove = [self indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { | |
return [obj object] == nil; | |
}]; | |
[self removeObjectsAtIndexes:toRemove]; | |
} | |
@end | |
@implementation SPWeakHolder | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment