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
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
500
Compilation Process
alexdenisov
5
800
Other Decks in Programming
See All in Programming
フロントエンドとバックエンドで「1文字」を揃えよう
youkidearitai
PRO
0
690
net-httpのHTTP/2対応について
naruse
0
490
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
680
TAKTでAI駆動開発の品質を設計する
j5ik2o
7
1.3k
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4k
Hunting Vulnerabilities in Symfony with LLMs
vinceamstoutz
0
540
軽量Java基盤の設計 DIコンテナに頼らない、長期保守と1秒起動の実現 JJUG CCC 2026 Spring
macha64
0
530
依存関係から依存物へ―Dependencyという言葉の歴史をひも解く
j_lee
0
120
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
260
メソッドのジェネリクスでGoの夢は広がるか? / Kyoto.go #65
utgwkk
3
780
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
260
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.6k
Featured
See All Featured
The Curse of the Amulet
leimatthew05
1
13k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.7k
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2k
GitHub's CSS Performance
jonrohan
1033
470k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
エンジニアに許された特別な時間の終わり
watany
107
250k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
160
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
1.7k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
320
Unsuck your backbone
ammeep
672
58k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
840
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?