Skip to content

Instantly share code, notes, and snippets.

@millenomi
Created February 27, 2011 13:34
Show Gist options
  • Save millenomi/846186 to your computer and use it in GitHub Desktop.
Save millenomi/846186 to your computer and use it in GitHub Desktop.
//
// ILService.h
// DependencyInjection
//
// Created by ∞ on 26/02/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol ILServicesProvider;
/**
A services set provides its clients references to a number of objects that provide functionality; services set can be used by either a portion of, or the whole of, the application. This class is used to simplify dependency injection, to an extent.
Typically, you will retain a services set as part of your implementation (typically passed in to your objects' constructor, or set externally through a property). Then, you can get references to the services by passing the service's defining protocol to objectConformingToProtocol:.
You can create service sets with the init method, and set their contents using the appropriate setter methods. Service sets are also hierarchical; if you want to offer some additional services to only a part of the application, you can create a new services set and set its alternateServices property to another set or service provider; the new set will appear to contain all objects its alternate services object provides, except it can add or override any class or protocol by setting a new object for it via the setter methods.
*/
@interface ILServicesSet : NSObject
/** The designated constructor for ILServicesSet. */
- (id) init;
/** Sets a specific object explicitly as conforming to the given protocol.
Setting an object for a specified protocol removes any previous object set for it, or any previous target/selector pair set through setTarget:selector:forProvidingObjectConformingToProtocol:.
@param obj The object to associate to this protocol. The object must conform to the specified protocol, or behavior will be undefined.
@param p The protocol to associate this object with.
*/
- (void) setObject:(id) obj conformingToProtocol:(Protocol*) p;
/** Sets a method that creates or returns an object conforming to the given protocol.
The target and selector are recorded, but not immediately invoked. The first time an object for the specified protocol is requested, they are invoked and the result is cached. The selector is not subsequently invoked.
Setting a target/selector pair for a specified protocol removes any previous target/selector pair, or any object set through setObject:conformingToProtocol:.
@param target The object that will provide the conforming instance.
@param selector A selector that, when sent to the target, will provide the object conforming to the given protocol. The target must respond this selector, which must take no arguments.
@param p The protocol the object returned by the selector will conform to.
*/
- (void) setTarget:(id) target selector:(SEL) selector forProvidingObjectConformingToProtocol:(Protocol*) p;
/** Retrieves an object conforming to the specified protocol if it was set through any of the setter methods, or passes the invocation to the alternateServices if set. If no object conforming to the specified protocol can be found, returns `nil`.
@param p The protocol to retrieve the object for.
*/
- (id) objectConformingToProtocol:(Protocol*) p;
/** An alternate services object is an instance that will be queried if this set cannot find an object conforming to the specified protocol.
Note that ILServicesSet conforms to the ILServicesProvider protocol, so you can use another ILServicesSet as a fallback. If this property is `nil`, no fallback occurs. Defaults to `nil`.
*/
@property(retain, nonatomic) id <ILServicesProvider> alternateServices;
@end
/**
A services provider is an object that returns service-providing instances according to the protocol they conform to. The ILServicesSet class conforms to this protocol, and can fall back to other instances that conform to it through the -[ILServicesSet alternateServices] property.
*/
@protocol ILServicesProvider <NSObject>
/** Retrieves an object conforming to the specified protocol. If no object conforming to the specified protocol can be found, returns `nil`.
@param p The protocol to retrieve the object for.
*/
- (id) objectConformingToProtocol:(Protocol*) p;
@end
//
// ILService.m
// DependencyInjection
//
// Created by ∞ on 26/02/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "ILServicesSet.h"
#import <objc/runtime.h>
@interface ILServicesSet ()
@property(retain, nonatomic) NSMutableDictionary* objectsByProtocol;
@property(retain, nonatomic) NSMutableDictionary* invocationsByProtocol;
@end
static NSString* ILNSStringFromProtocol(Protocol* p) {
return [NSString stringWithCString:protocol_getName(p) encoding:NSUTF8StringEncoding];
}
@implementation ILServicesSet
- (id)init
{
self = [super init];
if (self) {
self.objectsByProtocol = [NSMutableDictionary dictionary];
self.invocationsByProtocol = [NSMutableDictionary dictionary];
}
return self;
}
- (void)dealloc
{
self.invocationsByProtocol = nil;
self.objectsByProtocol = nil;
self.alternateServices = nil;
[super dealloc];
}
@synthesize alternateServices, objectsByProtocol, invocationsByProtocol;
- (void) setObject:(id) obj conformingToProtocol:(Protocol*) p;
{
NSString* protocolName = ILNSStringFromProtocol(p);
[self.objectsByProtocol setObject:obj forKey:protocolName];
[self.invocationsByProtocol removeObjectForKey:protocolName];
}
- (id) objectConformingToProtocol:(Protocol*) p;
{
NSString* protocolName = ILNSStringFromProtocol(p);
id x = [self.objectsByProtocol objectForKey:protocolName];
if (!x) {
NSInvocation* invo = [self.invocationsByProtocol objectForKey:protocolName];
[invo invoke];
[invo getReturnValue:&x];
if (x)
[self setObject:x conformingToProtocol:p]; // this implicitly removes the invocation.
}
if (!x)
x = [self.alternateServices objectConformingToProtocol:p];
return x;
}
- (void) setTarget:(id) target selector:(SEL) selector forProvidingObjectConformingToProtocol:(Protocol*) p;
{
NSMethodSignature* sig = [target methodSignatureForSelector:selector];
NSInvocation* invo = [NSInvocation invocationWithMethodSignature:sig];
[invo setTarget:target];
[invo setSelector:selector];
NSString* protocolName = ILNSStringFromProtocol(p);
[self.invocationsByProtocol setObject:invo forKey:protocolName];
[self.objectsByProtocol removeObjectForKey:protocolName];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment