#Objective IOC
Dependency injection for objective-c.
##How to use
- Just make instance of container, which should be retained somewhere (e.g. in application delegate).
- Fill created container with components. It can be application window or application delegate which you want have visible in every class in your application. But mainly add your main controller.
- Get main controller from your container. It will instantiate controller and fills it with his dependecies from container.
- Continue like in any other application.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.container = [[[MVIOCContainer alloc] init] autorelease];
//add application delegate into container
[self.container addComponent:self];
//add application window into container
[self.container addComponent:self.window];
//Add main controller into container
[self.container addComponent:[MainController class]];
//Get main controller instance from container
UIViewController *mainController =
(UIViewController *)[self.container getComponent:[MainController class]];
//place main controller view into window
[self.window addSubview:mainController.view];
[self.window makeKeyAndVisible];
return YES;
}
###Adding components into container There is many ways how to add components into container : ####Add component as instance.
This is especialy useful when you want to add application delegate or window
[container addComponent:instance]
####Add component as class.
[container addComponent:[SomeClass class]]
####Add component which represents some role.
This can be used when you have some protocol(role) and class(representer) which implement that protocol
[container addComponent:[SomeClass class] representing:@protocol(SomeProtocol)]
####Add component which should behave as singleton.
That means it is created just one time, and then just reused.
[[self.container withCache] addComponent:[SomeClass class]]
####Add component with custom init method. Useful when you want to use interface builder
[[self.container withInitSelector:@selector(initWithNibName:bundle:) params:@"NextController", [NSBundle mainBundle], nil] addComponent:[NextController class]];
####Add component with explicitly defined dependencies.
`NSDictionary *deps = [NSDictionary dictionaryWithObjectsAndKeys:[SomeColaborator class], @"colaborator", nil];`
[[self.container withDepsDictionary:deps] addComponent:[SomeClass class]];
###Getting components from container
Components should be returned from container on entry points. For example in the application:didFinishLaunchingWithOptions:
selector of application delegate can be used something like this:
UIViewController *mainController =
(UIViewController *)[self.container getComponent:[MainController class]];
[self.window addSubview:mainController.view];
All other components should be created throught injection as colaborators. Ideal situation is just one usage of getComponent method of container.
###Writing classes which can be placed into container
Great emphasis is placed on creating classes as usual. Do not to force users of this framework to inherit some special abstract class or implement protocol. So, how to make it:
//SomeController.h
@interface SomeController : UIViewController {
//autowire injection
SomeAppDelegate *injAppDelegate;
SomeService *service;
}
//make it public and to allow injection
@property(nonatomic, assign) SomeAppDelegate *appDelegate;
@property(nonatomic, retain) SomeService *service;
@property(nonatomic, retain) NextController *nextController;
@end
//SomeController.m
@implementation MainController
//inj prefix tell to container to use autowiring. So he lookup for 'SomeAppDelegate' component and inject it.
@synthesize appDelegate = injAppDelegate;
@synthesize service;
//when we use dynamic we are saying to container that we want lazy load. So collaborator is created in time when we want to use it.
@dynamic nextController;
@end
####What we have seen here:
#####Autowiring of components throught use of prefix 'inj' on instance variable member
Autowiring is the most preferable style of wiring components together.
Container take a look at variable member type and ask container for component of this type. Type can be specified as some class or protocol
#####Some service component which must be added in manualy
NSDictionary *deps = [NSDictionary dictionaryWithObjectsAndKeys:[SomeService class], @"service", nil];
[[self.container withDepsDictionary:deps] addComponent:[SomeController class]];
As you can see, autowiring and explicitly added in components can be mixed togehter. Dictionaries keys are names of properties and values are components representers, which are in container.
#####Using of lazy loading feature
By using @dynamic property
Lazy load can use autowiring too.
###Customization of Container
####Injection types
As default container uses property injection type when adding component. But there can be more injection types.
It is possible to say which type to use when adding component or it can be setted as default for all addings.
#####Add component with explicitly setted injection type
[[container withInjectionType:someInjectionType] addComponent:[SomeClass class]]
#####Set some injection type which is used as default
container.injectionType = someInjectionType
#####Container now supports two injection types
- Property injection type (default)
- [[Factory injection type]]
It is possible to write your own injection type. Just implement protocol and use it
####Caching of components
Caching of components is mainly used to simulate singleton behavior of components. As with injection types, there can be more caching strategies.
Framework now support just really easy caching which is in most cases good enough, but it is not thread safe. But other can be written, for example thread safe caching, or caching just in threads ...
How to use cache:
// using default cache strategy (not thread safe)
[[container withCache] addComponent:[SomeClass class]]
- git clone git://github.com/mivasi/Objective-IOC.git
- copy src/Classes into your project
- include header where you need
#import "MVIOC.h"