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