Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Dependency Injection FTW
Search
0xc010d
February 18, 2014
Programming
2
840
Dependency Injection FTW
cocoaheadsNL@Utrecht/Atos
0xc010d
February 18, 2014
Tweet
Share
More Decks by 0xc010d
See All by 0xc010d
Doing CI like a boss
0xc010d
2
380
Reversing 101
0xc010d
6
410
Other Decks in Programming
See All in Programming
ソフトウェアテスト徹底指南書の紹介
goyoki
1
150
Amazon RDS 向けに提供されている MCP Server と仕組みを調べてみた/jawsug-okayama-2025-aurora-mcp
takahashiikki
1
110
知っているようで知らない"rails new"の世界 / The World of "rails new" You Think You Know but Don't
luccafort
PRO
1
110
はじめてのMaterial3 Expressive
ym223
2
310
パッケージ設計の黒魔術/Kyoto.go#63
lufia
3
430
実用的なGOCACHEPROG実装をするために / golang.tokyo #40
mazrean
1
270
Azure SRE Agentで運用は楽になるのか?
kkamegawa
0
2.2k
[FEConf 2025] 모노레포 절망편, 14개 레포로 부활하기까지 걸린 1년
mmmaxkim
0
1.6k
Oracle Database Technology Night 92 Database Connection control FAN-AC
oracle4engineer
PRO
1
440
AI時代のUIはどこへ行く?
yusukebe
18
8.9k
「手軽で便利」に潜む罠。 Popover API を WCAG 2.2の視点で安全に使うには
taitotnk
0
850
Android端末で実現するオンデバイスLLM 2025
masayukisuda
1
150
Featured
See All Featured
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Typedesign – Prime Four
hannesfritz
42
2.8k
Balancing Empowerment & Direction
lara
3
620
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
How to Think Like a Performance Engineer
csswizardry
26
1.9k
Build The Right Thing And Hit Your Dates
maggiecrowley
37
2.9k
Reflections from 52 weeks, 52 projects
jeffersonlam
352
21k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
For a Future-Friendly Web
brad_frost
180
9.9k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
139
34k
How to train your dragon (web standard)
notwaldorf
96
6.2k
The Art of Programming - Codeland 2020
erikaheidi
56
13k
Transcript
Dependency Injection FTW Ievgen Solodovnykov @0xc010d
I… • work at 2dehands.be • do iOS stuff during
the daylight
Inversion of Control • M. Fowler, 1988 (http://martinfowler.com/bliki/ InversionOfControl.html) •
Don't call us, we'll call you • Dependency Injection is one of possible implementations
Dependency Injection • M. Fowler, 2004 (http://www.martinfowler.com/ articles/injection.html) • Helps
to get rid of hard coupling • Easier unit testing and motivates to write testable code • Improves modularity and reusability
Dependency Injection • Constructor injection • Property injection • Interface
injection + Automatic injection
Constructor injection @interface APIClient : NSObject - (void)loadDataWithCompletionBlock:(void(^)(NSError *))block; @end
! //... ! @implementation ViewController { APIClient *_apiClient; } ! - (instancetype)initWithAPIClient:(APIClient *)apiClient { self = [super init]; _apiClient = apiClient; return self; } ! - (void)viewDidLoad { [super viewDidLoad]; [_apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Property injection @interface ViewController : UIViewController ! @property (nonatomic, weak)
APIClient *apiClient; ! @end ! //... ! @implementation ViewController ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Method injection @implementation ViewController ! - (void)viewDidLoad { [super viewDidLoad];
[self loadDataWithAPIClient:[[APIClient alloc] init]]; } ! - (void)loadDataWithAPIClient:(APIClient *)apiClient { [apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Automatic injection with BloodMagic • https://github.com/railsware/BloodMagic • Runtime-based • Less
code • Custom initializers • And more…
BMLazy @interface ViewController () <BMLazy> ! @property (nonatomic, weak) APIClient
*apiClient; ! @end ! ! @implementation ViewController ! @dynamic apiClient; ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
BMLazy [[APIClient alloc] init] @interface ViewController () <BMLazy> ! @property
(nonatomic, weak) APIClient *apiClient; ! @end ! ! @implementation ViewController ! @dynamic apiClient; ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Custom initializer __attribute__((constructor)) void configureAPIClient(void) { BMInitializer *initializer = [BMInitializer
lazyInitializer]; initializer.propertyClass = [APIClient class]; initializer.initializer = ^id (id sender) { static dispatch_once_t onceToken; static APIClient *client; dispatch_once(&onceToken, ^{ client = [[APIClient alloc] init]; }); return client; }; [initializer registerInitializer]; }
Custom initializer { static dispatch_once_t onceToken; static APIClient *client; dispatch_once(&onceToken,
^{ client = [[APIClient alloc] init]; }); return client; }; @interface ViewController () <BMLazy> ! @property (nonatomic, weak) APIClient *apiClient; ! @end ! ! @implementation ViewController ! @dynamic apiClient; ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; } ! @end
Protocol binding @protocol APICLient <NSObject> ! - (void)loadDataWithCompletionBlock:(void (^)(NSError *))completionBlock;
! @end ! //--- ! @interface ProductionAPIClient : NSObject <APICLient> @end ! //--- ! @interface ViewController () <BMLazy> ! @property (nonatomic, weak) id<APICLient> apiClient; ! @end ! //--- ! @dynamic apiClient; ! - (void)viewDidLoad { [super viewDidLoad]; [self.apiClient loadDataWithCompletionBlock:^(NSError *) { //... }]; }
Protocol initializer __attribute__((constructor)) void configureProductionAPIClient(void) { BMInitializer *initializer = [BMInitializer
lazyInitializer]; initializer.protocols = @[ @protocol(APICLient) ]; initializer.initializer = ^id (id sender){ static dispatch_once_t onceToken; static ProductionAPIClient *client; dispatch_once(&onceToken, ^{ client = [[ProductionAPIClient alloc] init]; }); return client; }; [initializer registerInitializer]; } !
Container initializer @interface DemoViewController : ViewController @end ! @interface DemoAPIClient
: NSObject <APICLient> @end ! __attribute__((constructor)) void configureDemoAPIClient(void) { BMInitializer *initializer = [BMInitializer lazyInitializer]; initializer.protocols = @[ @protocol(APICLient) ]; initializer.containerClass = [DemoViewController class]; initializer.initializer = ^id (id sender){ static dispatch_once_t onceToken; static DemoAPIClient *client; dispatch_once(&onceToken, ^{ client = [[DemoAPIClient alloc] init]; }); return client; }; [initializer registerInitializer]; }
Disadvantages • Runtime injecting requires time • Runtime errors •
Requires some intelligence
Alternatives • Typhoon (http://www.typhoonframework.org) • Objection (http://objection-framework.org) • Xider a.k.a
LightMagic (@0xc010d)
Xider vs BloodMagic • Almost as fast as ivar assignment
• Less flexible implementation :(
Unit testing / with BloodMagic
Return value tests • Works fine with functions, often doesn’t
require injection • Call function/method with different parameters • Assert return value
State tests • Might require injection • Setup object •
Call method • Assert object property / other method return value
Interaction tests • Most often requires injection • Setup object
• Call method • Assert other object property / method return value
Demo
Thank you! https://speakerdeck.com/0xc010d/dependency-injection-ftw @0xc010d