SlideShare a Scribd company logo
BloodMagic 
Custom property attributes 
CocoaHeads, 2014
WHOAMI 
Github: 
AlexDenisov 
IRC: AlexDenisov 
1101_debian 
Twitter:
Outline 
• What BloodMagic is 
• How to use it 
• How to extend it 
• How does it work 
• Q & A
What BloodMagic is
Problem 
@interface ViewController : UIViewController 
@property (nonatomic) NSMutableArray *resources; 
@property (nonatomic) ProgressView *progressView; 
@property (nonatomic) ErrorView *errorView; 
@property (nonatomic) DataLoader *dataLoader; 
@end
Lazy Initialization 
@implementation ViewController 
- (NSMutableArray *)resources 
{ 
if (!_resources) { 
_resources = [NSMutableArray new]; 
} 
return _resources; 
} 
- (ProgressView *)progressView 
{ 
if (!_progressView) { 
_progressView = [ProgressView new]; 
} 
return _progressView; 
} 
- (ErrorView *)errorView 
{ 
if (!_errorView) { 
_errorView = [ErrorView new]; 
} 
return _errorView; 
} 
- (DataLoader *)dataLoader 
{ 
if (!_dataLoader) { 
_dataLoader = [DataLoader new]; 
} 
return _dataLoader; 
} 
@end
- (PropertyClass *)propertyName 
{ 
if (!_propertyName) { 
_propertyName = [PropertyClass new]; 
} 
return _propertyName; 
} 
A lot of Boilerplate Code
Just add ‘lazy’ attribute 
@interface ViewController : UIViewController 
@property (nonatomic, lazy) NSMutableArray *resources; 
@property (nonatomic, lazy) CenterProgress *centerProgress; 
@property (nonatomic, lazy) BottomProgress *bottomProgress; 
@property (nonatomic, lazy) DataLoader *dataLoader; 
@end
Just add ‘lazy’ attribute 
@interface ViewController : UIViewController 
@property (nonatomic, lazy) NSMutableArray *resources; 
@property (nonatomic, lazy) CenterProgress *centerProgress; 
@property (nonatomic, lazy) BottomProgress *bottomProgress; 
@property (nonatomic, lazy) DataLoader *dataLoader; 
@end 
/tmp/lazy_test ➜ make 
… 
error: unknown property attribute 'lazy' 
@property (nonatomic, lazy) NSMutableArray *resources; 
^ 
…
BloodMagic 
a mechanism for creating 
custom property attributes 
based on your code
Let’s add magic 
#import <BloodMagic/Lazy.h> 
@interface ViewController : UIViewController 
<BMLazy> 
@property (nonatomic) NSMutableArray *resources; 
@property (nonatomic) ProgressView *progressView; 
@property (nonatomic) ErrorView *errorView; 
@property (nonatomic) DataLoader *dataLoader; 
@end
Let’s add magic 
@implementation ViewController 
@dynamic resources; 
@dynamic progressView; 
@dynamic errorView; 
@dynamic dataLoader; 
- (void)actOnSomething 
{ 
[self.dataLoader loadNextPage]; 
// ^ new object created 
} 
@end
Magic Happened 
@implementation ViewController 
@dynamic resources; 
@dynamic progressView; 
@dynamic errorView; 
@dynamic dataLoader; 
@end 
BloodMagic 
@implementation ViewController 
- (NSMutableArray *)resources 
{ 
if (!_resources) { 
_resources = [NSMutableArray new]; 
} 
return _resources; 
} 
- (ProgressView *)progressView 
{ 
if (!_progressView) { 
_progressView = [ProgressView new]; 
} 
return _progressView; 
} 
- (ErrorView *)errorView 
{ 
if (!_errorView) { 
_errorView = [ErrorView new]; 
} 
return _errorView; 
} 
- (DataLoader *)dataLoader 
{ 
if (!_dataLoader) { 
_dataLoader = [DataLoader new]; 
} 
return _dataLoader; 
} 
@end
How to use 
BloodMagic
Single Custom Attribute 
#import <BloodMagic/Lazy.h> 
@interface ViewController : UIViewController 
<BMLazy> 
@property (nonatomic, bm_lazy) DataLoader *dataLoader; 
@end
Single Custom Attribute 
#import "ViewController.h" 
@implementation ViewController 
@dynamic dataLoader; 
- (void)viewDidLoad 
{ 
[super viewDidLoad]; 
[self.dataLoader loadNextPage]; 
} 
@end
Single Custom Attribute 
#import "ViewController.h" 
@implementation ViewController 
@dynamic dataLoader; 
- (void)viewDidLoad 
{ 
[super viewDidLoad]; 
[self.dataLoader loadNextPage]; 
// ^ new object created 
} 
@end
Multiple Custom Attributes 
#import <BloodMagic/Lazy.h> 
#import <BloodMagic/Partial.h> 
@interface ViewController : UIViewController 
<BMLazy, 
BMPartial> 
@property (nonatomic, bm_lazy) DataLoader *dataLoader; 
@property (nonatomic, bm_partial) ErrorView *errorView; 
@end
Multiple Custom Attributes 
#import "ViewController.h" 
@implementation ViewController 
@lazy(dataLoader) 
@partial(errorView) 
- (void)viewDidLoad 
{ 
[super viewDidLoaded]; 
[self.dataLoader loadNextPage]; 
[self.view addSubview:self.errorView]; 
} 
@end
How to extend it
@property (nonatomic, bm_preference) NSString *cocoaHeads;
Module structure 
BloodMagic/Sources/Modules/Preference (master ✔) ➜ tree 
|-- Private 
| |-- BMPreferenceHook.h 
| |-- BMPreferenceHook.m 
| |-- BMPreferenceModuleLoader.h 
| `-- BMPreferenceModuleLoader.m 
`-- Public 
`-- BMPreference.h 
… 
|-- BloodMagic/Sources 
`-- Preference.h
Public Protocol 
@protocol BMPreference 
<NSObject> 
@end
Public Header 
#import <BloodMagic/Sources/Modules/Core/Public/BMPublicCoreDefnitions.h> 
#import <BloodMagic/Sources/Modules/Preference/Public/BMPreference.h> 
#ifndef bm_preference 
#define bm_preference 
#endif 
#ifndef preference 
#define preference(property_name) register_module(BMPreference, property_name) 
#endif
Public Header 
#import <BloodMagic/Sources/Modules/Core/Public/BMPublicCoreDefnitions.h> 
#import <BloodMagic/Sources/Modules/Preference/Public/BMPreference.h> 
#ifndef bm_preference 
#define bm_preference 
#endif 
#ifndef preference 
#define preference(property_name) register_module(BMPreference, property_name) 
#endif
Private Module Loader 
#import <Foundation/Foundation.h> 
@interface BMPreferenceModuleLoader : NSObject 
@end
Private Module Loader 
#import "BMPreferenceModuleLoader.h" 
#import "BMPreference.h" 
#import "BMBloodMagicInjector.h" 
@implementation BMPreferenceModuleLoader 
+ (void)load 
{ 
@autoreleasepool { 
BMBloodMagicInjector *injector = [BMBloodMagicInjector new]; 
[injector injectBloodMagicInto:@protocol(BMPreference)]; 
} 
} 
@end
Private Hook 
#import "BMHook.h" 
#import "BMPreference.h" 
@interface BMPreferenceHook : NSObject 
<BMHook, 
BMPreference> 
@end
Private Hook 
#import "BMPreferenceHook.h" 
#import "BMProperty.h" 
@implementation BMPreferenceHook 
static inline NSUserDefaults *bm_defaults() 
{ 
return [NSUserDefaults standardUserDefaults]; 
} 
+ (void)accessorHook:(id *)value 
withProperty:(const BMProperty *)property 
sender:(__unused id)sender 
{ 
*value = [bm_defaults() objectForKey:property.name]; 
} 
+ (void)mutatorHook:(id *)value 
withProperty:(const BMProperty *)property 
sender:(__unused id)sender 
{ 
[bm_defaults() setObject:*value forKey:property.name]; 
} 
@end
Private Hook 
Accessor 
static inline NSUserDefaults *bm_defaults() 
{ 
return [NSUserDefaults standardUserDefaults]; 
} 
+ (void)accessorHook:(id *)value 
withProperty:(const BMProperty *)property 
sender:(__unused id)sender 
{ 
*value = [bm_defaults() objectForKey:property.name]; 
}
Private Hook 
Mutator 
static inline NSUserDefaults *bm_defaults() 
{ 
return [NSUserDefaults standardUserDefaults]; 
} 
+ (void)mutatorHook:(id *)value 
withProperty:(const BMProperty *)property 
sender:(__unused id)sender 
{ 
[bm_defaults() setObject:*value forKey:property.name]; 
}
BMPreference Usage 
#import <BloodMagic/Preference.h> 
@interface Settings : NSObject 
<BMPreference> 
@property (nonatomic, bm_preference) NSString *name; 
@property (nonatomic, bm_preference) NSUInteger age; 
@end
BMPreference Usage 
#import "Settings.h" 
@implementation Settings 
@dynamic name; 
@dynamic age; 
@end
BMPreference Usage 
Settings *settings = [Settings new]; 
settings.name = @"AlexDenisov"; 
settings.age = 26; 
// … 
NSLog(@"%@", defaults);
BMPreference Usage 
Settings *settings = [Settings new]; 
settings.name = @"AlexDenisov"; 
settings.age = 26; 
// … 
NSLog(@"%@", defaults); 
{ 
… 
age = 26; 
name = AlexDenisov; 
… 
}
How does it work
BloodMagic is modular 
~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 
. 
`-- Modules 
|-- Core 
|-- Final 
|-- Injectable 
|-- Initializers 
|-- Lazy 
|-- Partial 
`-- Preference
BloodMagic is modular 
~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 
. 
`-- Modules 
|-- Core 
|-- Final 
|-- Injectable 
|-- Initializers 
|-- Lazy 
|-- Partial 
`-- Preference 
Property attributes 
implementation
BloodMagic is modular 
~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 
. 
`-- Modules 
|-- Core 
|-- Final 
|-- Injectable 
|-- Initializers 
|-- Lazy 
|-- Partial 
`-- Preference 
Low level logic 
• hooks 
• injections 
• runtime routines
Core Module: Hooks 
@protocol BMHook 
<NSObject> 
@optional 
+ (void)mutatorHook:(id *)value 
withProperty:(const BMProperty *)property 
sender:(id)sender; 
+ (void)accessorHook:(id *)value 
withProperty:(const BMProperty *)property 
sender:(id)sender; 
@end
Core Module: Injections 
@interface BMBloodMagicInjector : NSObject 
- (void)injectBloodMagicInto:(Protocol *)protocol; 
@end
Core Module: Injections 
@implementation BMBloodMagicInjector 
// pseudocode 
- (void)injectBloodMagicInto:(Protocol *)protocol 
{ 
class_list_t classes = collectClasses(protocol); 
property_list_t properties = collectDynamicProperties(classes); 
for (Property *property in properties) { 
Hook *hook = hookForProperty(property); 
property.accessor = hook.accessor; 
property.mutator = hook.mutator; 
} 
} 
@end
Sources: 
https://github.com/railsware/BloodMagic 
Blog-post: 
http://l.rw.rw/dibm 
Slides: 
https://speakerdeck.com/alexdenisov/bloodmagic 
https://speakerdeck.com/0xc010d/dependency-injection-ftw
Questions?

More Related Content

PDF
Catalyst MVC
Sheeju Alex
 
PDF
MySQL Proxy
Manikanda kumar
 
KEY
Php Unit With Zend Framework Zendcon09
Michelangelo van Dam
 
PPT
Presentation
Manav Prasad
 
PDF
Getting Started-with-Laravel
Mindfire Solutions
 
PDF
Unit testing after Zend Framework 1.8
Michelangelo van Dam
 
KEY
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 
PDF
Spring MVC Annotations
Jordan Silva
 
Catalyst MVC
Sheeju Alex
 
MySQL Proxy
Manikanda kumar
 
Php Unit With Zend Framework Zendcon09
Michelangelo van Dam
 
Presentation
Manav Prasad
 
Getting Started-with-Laravel
Mindfire Solutions
 
Unit testing after Zend Framework 1.8
Michelangelo van Dam
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 
Spring MVC Annotations
Jordan Silva
 

What's hot (20)

PDF
What's Coming in Spring 3.0
Matt Raible
 
PDF
Riding Apache Camel
Apache Event Beijing
 
PDF
Perl web frameworks
diego_k
 
PDF
Apache Hive Hook
Minwoo Kim
 
PDF
JavaCro'14 - Scala and Java EE 7 Development Experiences – Peter Pilgrim
HUJAK - Hrvatska udruga Java korisnika / Croatian Java User Association
 
PDF
Apollo ecosystem
James Akwuh
 
PDF
«От экспериментов с инфраструктурой до внедрения в продакшен»​
FDConf
 
PDF
OGSA-DAI DQP: A Developer's View
Bartosz Dobrzelecki
 
PPTX
Read, store and create xml and json
Kim Berg Hansen
 
PDF
JAX-RS and CDI Bike the (Reactive) Bridge
José Paumard
 
PDF
JavaCro'14 - Unit testing in AngularJS – Slaven Tomac
HUJAK - Hrvatska udruga Java korisnika / Croatian Java User Association
 
PDF
Using the new WordPress REST API
Caldera Labs
 
PDF
Extending the WordPress REST API - Josh Pollock
Caldera Labs
 
PDF
Connecting Content Silos: One CMS, Many Sites With The WordPress REST API
Caldera Labs
 
PDF
Reporting solutions for ADF Applications
Getting value from IoT, Integration and Data Analytics
 
PDF
Redux. From twitter hype to production
FDConf
 
PDF
Rails 6 frontend frameworks
Eric Guo
 
PDF
the Spring 4 update
Joshua Long
 
PPT
Java Server Pages
BG Java EE Course
 
PPT
Data Access with JDBC
BG Java EE Course
 
What's Coming in Spring 3.0
Matt Raible
 
Riding Apache Camel
Apache Event Beijing
 
Perl web frameworks
diego_k
 
Apache Hive Hook
Minwoo Kim
 
JavaCro'14 - Scala and Java EE 7 Development Experiences – Peter Pilgrim
HUJAK - Hrvatska udruga Java korisnika / Croatian Java User Association
 
Apollo ecosystem
James Akwuh
 
«От экспериментов с инфраструктурой до внедрения в продакшен»​
FDConf
 
OGSA-DAI DQP: A Developer's View
Bartosz Dobrzelecki
 
Read, store and create xml and json
Kim Berg Hansen
 
JAX-RS and CDI Bike the (Reactive) Bridge
José Paumard
 
JavaCro'14 - Unit testing in AngularJS – Slaven Tomac
HUJAK - Hrvatska udruga Java korisnika / Croatian Java User Association
 
Using the new WordPress REST API
Caldera Labs
 
Extending the WordPress REST API - Josh Pollock
Caldera Labs
 
Connecting Content Silos: One CMS, Many Sites With The WordPress REST API
Caldera Labs
 
Reporting solutions for ADF Applications
Getting value from IoT, Integration and Data Analytics
 
Redux. From twitter hype to production
FDConf
 
Rails 6 frontend frameworks
Eric Guo
 
the Spring 4 update
Joshua Long
 
Java Server Pages
BG Java EE Course
 
Data Access with JDBC
BG Java EE Course
 
Ad

Similar to Blood magic (20)

PDF
How to replace rails asset pipeline with webpack?
Tomasz Bak
 
PDF
Web applications with Catalyst
svilen.ivanov
 
PDF
Having Fun with Play
Clinton Dreisbach
 
PPTX
Magento Live Australia 2016: Request Flow
Vrann Tulika
 
PDF
Curscatalyst
Kar Juan
 
PDF
Java Web Programming [5/9] : EL, JSTL and Custom Tags
IMC Institute
 
PDF
Mojolicious
Marcos Rebelo
 
PDF
dokumen.tips_rediscovering-spring-with-spring-boot1 (1).pdf
Appster1
 
PDF
dokumen.tips_rediscovering-spring-with-spring-boot1.pdf
Appster1
 
PDF
JavaServer Faces 2.0 - JavaOne India 2011
Arun Gupta
 
PDF
Rediscovering Spring with Spring Boot(1)
Gunith Devasurendra
 
KEY
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
PDF
Apache Con Us2007 Apachei Batis
day
 
PPTX
Laravel5 Introduction and essentials
Pramod Kadam
 
PDF
Network Automation: Ansible 102
APNIC
 
PPTX
Let's react - Meetup
RAJNISH KATHAROTIYA
 
PDF
Symfony2 from the Trenches
Jonathan Wage
 
PDF
Rails 3 overview
Yehuda Katz
 
PDF
Symfony2 - from the trenches
Lukas Smith
 
PDF
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 
How to replace rails asset pipeline with webpack?
Tomasz Bak
 
Web applications with Catalyst
svilen.ivanov
 
Having Fun with Play
Clinton Dreisbach
 
Magento Live Australia 2016: Request Flow
Vrann Tulika
 
Curscatalyst
Kar Juan
 
Java Web Programming [5/9] : EL, JSTL and Custom Tags
IMC Institute
 
Mojolicious
Marcos Rebelo
 
dokumen.tips_rediscovering-spring-with-spring-boot1 (1).pdf
Appster1
 
dokumen.tips_rediscovering-spring-with-spring-boot1.pdf
Appster1
 
JavaServer Faces 2.0 - JavaOne India 2011
Arun Gupta
 
Rediscovering Spring with Spring Boot(1)
Gunith Devasurendra
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
Apache Con Us2007 Apachei Batis
day
 
Laravel5 Introduction and essentials
Pramod Kadam
 
Network Automation: Ansible 102
APNIC
 
Let's react - Meetup
RAJNISH KATHAROTIYA
 
Symfony2 from the Trenches
Jonathan Wage
 
Rails 3 overview
Yehuda Katz
 
Symfony2 - from the trenches
Lukas Smith
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 
Ad

Recently uploaded (20)

PPTX
Explanation about Structures in C language.pptx
Veeral Rathod
 
PPTX
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
PDF
Teaching Reproducibility and Embracing Variability: From Floating-Point Exper...
University of Rennes, INSA Rennes, Inria/IRISA, CNRS
 
PDF
ShowUs: Pharo Stream Deck (ESUG 2025, Gdansk)
ESUG
 
PPTX
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PDF
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
PDF
Appium Automation Testing Tutorial PDF: Learn Mobile Testing in 7 Days
jamescantor38
 
PDF
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
PPT
Activate_Methodology_Summary presentatio
annapureddyn
 
PDF
An Experience-Based Look at AI Lead Generation Pricing, Features & B2B Results
Thomas albart
 
PPTX
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
PDF
lesson-2-rules-of-netiquette.pdf.bshhsjdj
jasmenrojas249
 
PPTX
slidesgo-unlocking-the-code-the-dynamic-dance-of-variables-and-constants-2024...
kr2589474
 
PPTX
Can You Build Dashboards Using Open Source Visualization Tool.pptx
Varsha Nayak
 
PDF
Bandai Playdia The Book - David Glotz
BluePanther6
 
PPTX
Presentation about Database and Database Administrator
abhishekchauhan86963
 
PPTX
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
PDF
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
PDF
Protecting the Digital World Cyber Securit
dnthakkar16
 
Explanation about Structures in C language.pptx
Veeral Rathod
 
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
Teaching Reproducibility and Embracing Variability: From Floating-Point Exper...
University of Rennes, INSA Rennes, Inria/IRISA, CNRS
 
ShowUs: Pharo Stream Deck (ESUG 2025, Gdansk)
ESUG
 
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
Appium Automation Testing Tutorial PDF: Learn Mobile Testing in 7 Days
jamescantor38
 
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
Activate_Methodology_Summary presentatio
annapureddyn
 
An Experience-Based Look at AI Lead Generation Pricing, Features & B2B Results
Thomas albart
 
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
lesson-2-rules-of-netiquette.pdf.bshhsjdj
jasmenrojas249
 
slidesgo-unlocking-the-code-the-dynamic-dance-of-variables-and-constants-2024...
kr2589474
 
Can You Build Dashboards Using Open Source Visualization Tool.pptx
Varsha Nayak
 
Bandai Playdia The Book - David Glotz
BluePanther6
 
Presentation about Database and Database Administrator
abhishekchauhan86963
 
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
Protecting the Digital World Cyber Securit
dnthakkar16
 

Blood magic

  • 1. BloodMagic Custom property attributes CocoaHeads, 2014
  • 2. WHOAMI Github: AlexDenisov IRC: AlexDenisov 1101_debian Twitter:
  • 3. Outline • What BloodMagic is • How to use it • How to extend it • How does it work • Q & A
  • 5. Problem @interface ViewController : UIViewController @property (nonatomic) NSMutableArray *resources; @property (nonatomic) ProgressView *progressView; @property (nonatomic) ErrorView *errorView; @property (nonatomic) DataLoader *dataLoader; @end
  • 6. Lazy Initialization @implementation ViewController - (NSMutableArray *)resources { if (!_resources) { _resources = [NSMutableArray new]; } return _resources; } - (ProgressView *)progressView { if (!_progressView) { _progressView = [ProgressView new]; } return _progressView; } - (ErrorView *)errorView { if (!_errorView) { _errorView = [ErrorView new]; } return _errorView; } - (DataLoader *)dataLoader { if (!_dataLoader) { _dataLoader = [DataLoader new]; } return _dataLoader; } @end
  • 7. - (PropertyClass *)propertyName { if (!_propertyName) { _propertyName = [PropertyClass new]; } return _propertyName; } A lot of Boilerplate Code
  • 8. Just add ‘lazy’ attribute @interface ViewController : UIViewController @property (nonatomic, lazy) NSMutableArray *resources; @property (nonatomic, lazy) CenterProgress *centerProgress; @property (nonatomic, lazy) BottomProgress *bottomProgress; @property (nonatomic, lazy) DataLoader *dataLoader; @end
  • 9. Just add ‘lazy’ attribute @interface ViewController : UIViewController @property (nonatomic, lazy) NSMutableArray *resources; @property (nonatomic, lazy) CenterProgress *centerProgress; @property (nonatomic, lazy) BottomProgress *bottomProgress; @property (nonatomic, lazy) DataLoader *dataLoader; @end /tmp/lazy_test ➜ make … error: unknown property attribute 'lazy' @property (nonatomic, lazy) NSMutableArray *resources; ^ …
  • 10. BloodMagic a mechanism for creating custom property attributes based on your code
  • 11. Let’s add magic #import <BloodMagic/Lazy.h> @interface ViewController : UIViewController <BMLazy> @property (nonatomic) NSMutableArray *resources; @property (nonatomic) ProgressView *progressView; @property (nonatomic) ErrorView *errorView; @property (nonatomic) DataLoader *dataLoader; @end
  • 12. Let’s add magic @implementation ViewController @dynamic resources; @dynamic progressView; @dynamic errorView; @dynamic dataLoader; - (void)actOnSomething { [self.dataLoader loadNextPage]; // ^ new object created } @end
  • 13. Magic Happened @implementation ViewController @dynamic resources; @dynamic progressView; @dynamic errorView; @dynamic dataLoader; @end BloodMagic @implementation ViewController - (NSMutableArray *)resources { if (!_resources) { _resources = [NSMutableArray new]; } return _resources; } - (ProgressView *)progressView { if (!_progressView) { _progressView = [ProgressView new]; } return _progressView; } - (ErrorView *)errorView { if (!_errorView) { _errorView = [ErrorView new]; } return _errorView; } - (DataLoader *)dataLoader { if (!_dataLoader) { _dataLoader = [DataLoader new]; } return _dataLoader; } @end
  • 14. How to use BloodMagic
  • 15. Single Custom Attribute #import <BloodMagic/Lazy.h> @interface ViewController : UIViewController <BMLazy> @property (nonatomic, bm_lazy) DataLoader *dataLoader; @end
  • 16. Single Custom Attribute #import "ViewController.h" @implementation ViewController @dynamic dataLoader; - (void)viewDidLoad { [super viewDidLoad]; [self.dataLoader loadNextPage]; } @end
  • 17. Single Custom Attribute #import "ViewController.h" @implementation ViewController @dynamic dataLoader; - (void)viewDidLoad { [super viewDidLoad]; [self.dataLoader loadNextPage]; // ^ new object created } @end
  • 18. Multiple Custom Attributes #import <BloodMagic/Lazy.h> #import <BloodMagic/Partial.h> @interface ViewController : UIViewController <BMLazy, BMPartial> @property (nonatomic, bm_lazy) DataLoader *dataLoader; @property (nonatomic, bm_partial) ErrorView *errorView; @end
  • 19. Multiple Custom Attributes #import "ViewController.h" @implementation ViewController @lazy(dataLoader) @partial(errorView) - (void)viewDidLoad { [super viewDidLoaded]; [self.dataLoader loadNextPage]; [self.view addSubview:self.errorView]; } @end
  • 21. @property (nonatomic, bm_preference) NSString *cocoaHeads;
  • 22. Module structure BloodMagic/Sources/Modules/Preference (master ✔) ➜ tree |-- Private | |-- BMPreferenceHook.h | |-- BMPreferenceHook.m | |-- BMPreferenceModuleLoader.h | `-- BMPreferenceModuleLoader.m `-- Public `-- BMPreference.h … |-- BloodMagic/Sources `-- Preference.h
  • 23. Public Protocol @protocol BMPreference <NSObject> @end
  • 24. Public Header #import <BloodMagic/Sources/Modules/Core/Public/BMPublicCoreDefnitions.h> #import <BloodMagic/Sources/Modules/Preference/Public/BMPreference.h> #ifndef bm_preference #define bm_preference #endif #ifndef preference #define preference(property_name) register_module(BMPreference, property_name) #endif
  • 25. Public Header #import <BloodMagic/Sources/Modules/Core/Public/BMPublicCoreDefnitions.h> #import <BloodMagic/Sources/Modules/Preference/Public/BMPreference.h> #ifndef bm_preference #define bm_preference #endif #ifndef preference #define preference(property_name) register_module(BMPreference, property_name) #endif
  • 26. Private Module Loader #import <Foundation/Foundation.h> @interface BMPreferenceModuleLoader : NSObject @end
  • 27. Private Module Loader #import "BMPreferenceModuleLoader.h" #import "BMPreference.h" #import "BMBloodMagicInjector.h" @implementation BMPreferenceModuleLoader + (void)load { @autoreleasepool { BMBloodMagicInjector *injector = [BMBloodMagicInjector new]; [injector injectBloodMagicInto:@protocol(BMPreference)]; } } @end
  • 28. Private Hook #import "BMHook.h" #import "BMPreference.h" @interface BMPreferenceHook : NSObject <BMHook, BMPreference> @end
  • 29. Private Hook #import "BMPreferenceHook.h" #import "BMProperty.h" @implementation BMPreferenceHook static inline NSUserDefaults *bm_defaults() { return [NSUserDefaults standardUserDefaults]; } + (void)accessorHook:(id *)value withProperty:(const BMProperty *)property sender:(__unused id)sender { *value = [bm_defaults() objectForKey:property.name]; } + (void)mutatorHook:(id *)value withProperty:(const BMProperty *)property sender:(__unused id)sender { [bm_defaults() setObject:*value forKey:property.name]; } @end
  • 30. Private Hook Accessor static inline NSUserDefaults *bm_defaults() { return [NSUserDefaults standardUserDefaults]; } + (void)accessorHook:(id *)value withProperty:(const BMProperty *)property sender:(__unused id)sender { *value = [bm_defaults() objectForKey:property.name]; }
  • 31. Private Hook Mutator static inline NSUserDefaults *bm_defaults() { return [NSUserDefaults standardUserDefaults]; } + (void)mutatorHook:(id *)value withProperty:(const BMProperty *)property sender:(__unused id)sender { [bm_defaults() setObject:*value forKey:property.name]; }
  • 32. BMPreference Usage #import <BloodMagic/Preference.h> @interface Settings : NSObject <BMPreference> @property (nonatomic, bm_preference) NSString *name; @property (nonatomic, bm_preference) NSUInteger age; @end
  • 33. BMPreference Usage #import "Settings.h" @implementation Settings @dynamic name; @dynamic age; @end
  • 34. BMPreference Usage Settings *settings = [Settings new]; settings.name = @"AlexDenisov"; settings.age = 26; // … NSLog(@"%@", defaults);
  • 35. BMPreference Usage Settings *settings = [Settings new]; settings.name = @"AlexDenisov"; settings.age = 26; // … NSLog(@"%@", defaults); { … age = 26; name = AlexDenisov; … }
  • 36. How does it work
  • 37. BloodMagic is modular ~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 . `-- Modules |-- Core |-- Final |-- Injectable |-- Initializers |-- Lazy |-- Partial `-- Preference
  • 38. BloodMagic is modular ~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 . `-- Modules |-- Core |-- Final |-- Injectable |-- Initializers |-- Lazy |-- Partial `-- Preference Property attributes implementation
  • 39. BloodMagic is modular ~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 . `-- Modules |-- Core |-- Final |-- Injectable |-- Initializers |-- Lazy |-- Partial `-- Preference Low level logic • hooks • injections • runtime routines
  • 40. Core Module: Hooks @protocol BMHook <NSObject> @optional + (void)mutatorHook:(id *)value withProperty:(const BMProperty *)property sender:(id)sender; + (void)accessorHook:(id *)value withProperty:(const BMProperty *)property sender:(id)sender; @end
  • 41. Core Module: Injections @interface BMBloodMagicInjector : NSObject - (void)injectBloodMagicInto:(Protocol *)protocol; @end
  • 42. Core Module: Injections @implementation BMBloodMagicInjector // pseudocode - (void)injectBloodMagicInto:(Protocol *)protocol { class_list_t classes = collectClasses(protocol); property_list_t properties = collectDynamicProperties(classes); for (Property *property in properties) { Hook *hook = hookForProperty(property); property.accessor = hook.accessor; property.mutator = hook.mutator; } } @end
  • 43. Sources: https://github.com/railsware/BloodMagic Blog-post: http://l.rw.rw/dibm Slides: https://speakerdeck.com/alexdenisov/bloodmagic https://speakerdeck.com/0xc010d/dependency-injection-ftw