Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
BloodMagic
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
AlexDenisov
April 11, 2014
Programming
1k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
BloodMagic
CocoaHeads, 2014
https://github.com/railsware/BloodMagic
AlexDenisov
April 11, 2014
More Decks by AlexDenisov
See All by AlexDenisov
Getting started with LLVM using Swift
alexdenisov
0
190
Magic Behind Xcode
alexdenisov
2
520
Compilation Process
alexdenisov
5
810
Other Decks in Programming
See All in Programming
Vue.jsで作る空間解析アプリとCapacitorプラグインのData Bridge
hal_spidernight
1
110
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
R8 の設定見直しでアプリサイズ削減
andpad
0
230
学びを記事・スライド・Xへ高速展開する― AI-assisted Learning Loopで回すコンテンツ生成術
ochtum
0
150
Inertia Rails: The One-Person Framework with React & Friends
skryukov
0
130
月間100万人のサービス刷新で、 技術選定をHonoに振り切った話
yutobara
8
7k
作れる、でも消せるか?──安全に「枯らして消す」機能削除術 / Drain, Then Delete
chobishiba
2
1k
PumaからFalconに移行したRailsアプリケーションでの実践
falcon8823
0
250
GemmaをJevのように使ってみる / Use Gemma like Jev
kishida
5
840
Railsのためのパスキー実践入門 〜マルチテナントSaaSでの意思決定〜
utakaha
3
1.3k
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
4
3.2k
S3 TablesのIcebergデータに、AIエージェントからアクセスしてみた / 20261016developersio2026joetsu-accessing-iceberg-in-s3tables-via-agent
kasacchiful
PRO
0
210
Featured
See All Featured
Facilitating Awesome Meetings
lara
57
7.2k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.8k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
940
Google's AI Overviews - The New Search
badams
0
1.6k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.4k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
590
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
1
470
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
35
2.9k
The Cost Of JavaScript in 2023
addyosmani
55
10k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
450
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3.5k
The Limits of Empathy - UXLibs8
cassininazir
2
730
Transcript
BloodMagic Custom property attributes CocoaHeads, 2014
AlexDenisov 1101_debian WHOAMI Twitter: Github: IRC: AlexDenisov
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
@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 Lazy Initialization
- (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 Slides: https://speakerdeck.com/alexdenisov/bloodmagic http://l.rw.rw/dibm Blog-post: https://speakerdeck.com/0xc010d/dependency-injection-ftw
Questions?