Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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 (nonato...
Lazy Initialization 
@implementation ViewController 
- (NSMutableArray *)resources 
{ 
if (!_resources) { 
_resources = [N...
- (PropertyClass *)propertyName 
{ 
if (!_propertyName) { 
_propertyName = [PropertyClass new]; 
} 
return _propertyName; ...
Just add ‘lazy’ attribute 
@interface ViewController : UIViewController 
@property (nonatomic, lazy) NSMutableArray *resou...
Just add ‘lazy’ attribute 
@interface ViewController : UIViewController 
@property (nonatomic, lazy) NSMutableArray *resou...
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...
Let’s add magic 
@implementation ViewController 
@dynamic resources; 
@dynamic progressView; 
@dynamic errorView; 
@dynami...
Magic Happened 
@implementation ViewController 
@dynamic resources; 
@dynamic progressView; 
@dynamic errorView; 
@dynamic...
How to use 
BloodMagic
Single Custom Attribute 
#import <BloodMagic/Lazy.h> 
@interface ViewController : UIViewController 
<BMLazy> 
@property (n...
Single Custom Attribute 
#import "ViewController.h" 
@implementation ViewController 
@dynamic dataLoader; 
- (void)viewDid...
Single Custom Attribute 
#import "ViewController.h" 
@implementation ViewController 
@dynamic dataLoader; 
- (void)viewDid...
Multiple Custom Attributes 
#import <BloodMagic/Lazy.h> 
#import <BloodMagic/Partial.h> 
@interface ViewController : UIVie...
Multiple Custom Attributes 
#import "ViewController.h" 
@implementation ViewController 
@lazy(dataLoader) 
@partial(errorV...
How to extend it
@property (nonatomic, bm_preference) NSString *cocoaHeads;
Module structure 
BloodMagic/Sources/Modules/Preference (master ✔) ➜ tree 
|-- Private 
| |-- BMPreferenceHook.h 
| |-- BM...
Public Protocol 
@protocol BMPreference 
<NSObject> 
@end
Public Header 
#import <BloodMagic/Sources/Modules/Core/Public/BMPublicCoreDefnitions.h> 
#import <BloodMagic/Sources/Modu...
Public Header 
#import <BloodMagic/Sources/Modules/Core/Public/BMPublicCoreDefnitions.h> 
#import <BloodMagic/Sources/Modu...
Private Module Loader 
#import <Foundation/Foundation.h> 
@interface BMPreferenceModuleLoader : NSObject 
@end
Private Module Loader 
#import "BMPreferenceModuleLoader.h" 
#import "BMPreference.h" 
#import "BMBloodMagicInjector.h" 
@...
Private Hook 
#import "BMHook.h" 
#import "BMPreference.h" 
@interface BMPreferenceHook : NSObject 
<BMHook, 
BMPreference...
Private Hook 
#import "BMPreferenceHook.h" 
#import "BMProperty.h" 
@implementation BMPreferenceHook 
static inline NSUser...
Private Hook 
Accessor 
static inline NSUserDefaults *bm_defaults() 
{ 
return [NSUserDefaults standardUserDefaults]; 
} 
...
Private Hook 
Mutator 
static inline NSUserDefaults *bm_defaults() 
{ 
return [NSUserDefaults standardUserDefaults]; 
} 
+...
BMPreference Usage 
#import <BloodMagic/Preference.h> 
@interface Settings : NSObject 
<BMPreference> 
@property (nonatomi...
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...
BMPreference Usage 
Settings *settings = [Settings new]; 
settings.name = @"AlexDenisov"; 
settings.age = 26; 
// … 
NSLog...
How does it work
BloodMagic is modular 
~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 
. 
`-- Modules 
|-- Core 
|-- Final 
|-- Inje...
BloodMagic is modular 
~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 
. 
`-- Modules 
|-- Core 
|-- Final 
|-- Inje...
BloodMagic is modular 
~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 
. 
`-- Modules 
|-- Core 
|-- Final 
|-- Inje...
Core Module: Hooks 
@protocol BMHook 
<NSObject> 
@optional 
+ (void)mutatorHook:(id *)value 
withProperty:(const BMProper...
Core Module: Injections 
@interface BMBloodMagicInjector : NSObject 
- (void)injectBloodMagicInto:(Protocol *)protocol; 
@...
Core Module: Injections 
@implementation BMBloodMagicInjector 
// pseudocode 
- (void)injectBloodMagicInto:(Protocol *)pro...
Sources: 
https://github.com/railsware/BloodMagic 
Blog-post: 
http://l.rw.rw/dibm 
Slides: 
https://speakerdeck.com/alexd...
Questions?
Upcoming SlideShare
Loading in …5
×

Blood magic

362 views

Published on

Published in: Software, Business, Technology
  • Be the first to comment

  • Be the first to like this

Blood magic

  1. 1. BloodMagic Custom property attributes CocoaHeads, 2014
  2. 2. WHOAMI Github: AlexDenisov IRC: AlexDenisov 1101_debian Twitter:
  3. 3. Outline • What BloodMagic is • How to use it • How to extend it • How does it work • Q & A
  4. 4. What BloodMagic is
  5. 5. Problem @interface ViewController : UIViewController @property (nonatomic) NSMutableArray *resources; @property (nonatomic) ProgressView *progressView; @property (nonatomic) ErrorView *errorView; @property (nonatomic) DataLoader *dataLoader; @end
  6. 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. 7. - (PropertyClass *)propertyName { if (!_propertyName) { _propertyName = [PropertyClass new]; } return _propertyName; } A lot of Boilerplate Code
  8. 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. 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. 10. BloodMagic a mechanism for creating custom property attributes based on your code
  11. 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. 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. 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. 14. How to use BloodMagic
  15. 15. Single Custom Attribute #import <BloodMagic/Lazy.h> @interface ViewController : UIViewController <BMLazy> @property (nonatomic, bm_lazy) DataLoader *dataLoader; @end
  16. 16. Single Custom Attribute #import "ViewController.h" @implementation ViewController @dynamic dataLoader; - (void)viewDidLoad { [super viewDidLoad]; [self.dataLoader loadNextPage]; } @end
  17. 17. Single Custom Attribute #import "ViewController.h" @implementation ViewController @dynamic dataLoader; - (void)viewDidLoad { [super viewDidLoad]; [self.dataLoader loadNextPage]; // ^ new object created } @end
  18. 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. 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
  20. 20. How to extend it
  21. 21. @property (nonatomic, bm_preference) NSString *cocoaHeads;
  22. 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. 23. Public Protocol @protocol BMPreference <NSObject> @end
  24. 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. 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. 26. Private Module Loader #import <Foundation/Foundation.h> @interface BMPreferenceModuleLoader : NSObject @end
  27. 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. 28. Private Hook #import "BMHook.h" #import "BMPreference.h" @interface BMPreferenceHook : NSObject <BMHook, BMPreference> @end
  29. 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. 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. 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. 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. 33. BMPreference Usage #import "Settings.h" @implementation Settings @dynamic name; @dynamic age; @end
  34. 34. BMPreference Usage Settings *settings = [Settings new]; settings.name = @"AlexDenisov"; settings.age = 26; // … NSLog(@"%@", defaults);
  35. 35. BMPreference Usage Settings *settings = [Settings new]; settings.name = @"AlexDenisov"; settings.age = 26; // … NSLog(@"%@", defaults); { … age = 26; name = AlexDenisov; … }
  36. 36. How does it work
  37. 37. BloodMagic is modular ~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 . `-- Modules |-- Core |-- Final |-- Injectable |-- Initializers |-- Lazy |-- Partial `-- Preference
  38. 38. BloodMagic is modular ~/Projects/BloodMagic/Sources (master ✔) ➜ tree -L 2 . `-- Modules |-- Core |-- Final |-- Injectable |-- Initializers |-- Lazy |-- Partial `-- Preference Property attributes implementation
  39. 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. 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. 41. Core Module: Injections @interface BMBloodMagicInjector : NSObject - (void)injectBloodMagicInto:(Protocol *)protocol; @end
  42. 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. 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
  44. 44. Questions?

×