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
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
180
Magic Behind Xcode
alexdenisov
2
520
Compilation Process
alexdenisov
5
810
Other Decks in Programming
See All in Programming
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
260
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
1
2k
Agents on Rails - Rails at Scale 2026
irinanazarova
0
310
JPUG勉強会 OSSデータベースの内部構造を理解しよう(第2回)
oga5
0
280
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
330
Embedded Swiftで作る自作USBデバイス によるiOSデバイスの自動テスト / iOSDC Japan 2026 glassfiber
glassfiber
0
240
Apple Intelligence を用いた個人情報誤送信防止、及びユーザーリクエスト体験の改善について
yukiny
0
260
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
7
2.3k
フロントエンドUIフレームワークのこれまでとこれから
ssssota
5
3.1k
Vue Fes Japan 2026 タイムテーブル徹底解説
448jp
1
580
AI時代のコードレビューは人に向けるな、仕組みに向けろ
texmeijin
5
3.2k
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
320
Featured
See All Featured
The SEO Collaboration Effect
kristinabergwall1
1
580
Fireside Chat
paigeccino
43
4k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
550
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
420
WENDY [Excerpt]
tessaabrams
14
40k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Balancing Empowerment & Direction
lara
6
1.3k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
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?