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
BloodMagic
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
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
170
Magic Behind Xcode
alexdenisov
2
510
Compilation Process
alexdenisov
5
800
Other Decks in Programming
See All in Programming
Haskell/Servantを通してWebミドルウェアを捉え直す
pizzacat83
1
580
LaravelLive Japan の裏方のすべて — 第188回 PHP勉強会@東京 (2026-06-24)
suguruooki
2
150
任せる範囲はこう広がった / How the Scope of AI Delegation Has Expanded
nrslib
1
260
Even G2とAWSで推しのエージェントを召喚しよう!
har1101
1
170
才能?センス?知らん、 続けたもん勝ちだ。-- 結婚・出産・癌を越えてなお、私がプロダクトを創り続ける理由
16bitidol
2
860
「正の参照」と 「負の導出」で組む ハーネスエンジニアリング
cottpan
1
140
トークンをケチるな、設計しろ:GitHub Copilotを賢く使うコンテキスト戦略
ochtum
0
320
Hatena Engineer Seminar #37「言語モデルの活用に関する研究」
slashnephy
0
520
これからAgentCoreを触る方へトレンドはGatewayです
har1101
6
490
コーディングルールの鮮度を保ちたい for SRE NEXT 2026 / keep-fresh-go-internal-conventions-sre-next-2026
handlename
0
140
Claude Opus 4.6以後の受託開発エンジニアの変化(Claude Code開発ノウハウ大公開スペシャルbyクラスメソッド)
iidatakuma
1
770
【やさしく解説 設計編 #1】「ドメイン駆動」と「実装駆動」ってなに? 〜設計の考え方を、たとえ話で学ぼう〜
panda728
PRO
1
120
Featured
See All Featured
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
340
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
390
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.1k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
390
We Are The Robots
honzajavorek
0
280
The Invisible Side of Design
smashingmag
301
52k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
67
56k
KATA
mclloyd
PRO
35
15k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
460
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?