KVOExt 0.4.0

KVOExt 0.4.0

测试测试过
Lang语言 Obj-CObjective C
许可证 MIT
发布最后发布2019 年 10 月

Alexander Stepanov 维护。



KVOExt 0.4.0

  • Alexander Stepanov

KVOExt

Build Status codebeat badge

简化 KVO 的工作。

特性

  • 简单语法
  • 强类型 keypath
  • 自动观察者移除
  • 处理块隐式由监听器保留
  • 处理块不保留 self
  • 支持延迟绑定
  • 可以替换代理、目标-动作模式
  • 非线程安全(仅在主线程上工作)

使用

绑定/观察

observe(src, propName) { ... }
bind(src, propName) { ... }

观察 - 开始监听 src.propName,当名为 propName 的属性设置器被调用时将触发处理块。
绑定 - 开始监听 src.propName 并立即触发处理块。在处理块中使用关键字 来访问可观察的值。

手动解绑

bind(src, propName, key) { ... }
unbind(key)

- 类似于字典的键,应符合 NSCopying 规范,通常是字符串。

懒加载绑定

使用类名代替源对象进行懒加载绑定。

bind(ClsName, propName) { ... }
self.dataContext = src;

在 dataContext 分配后绑定 "激活"。
dataContext 在绑定声明之前可以设置。
src 类应该是 ClsName 类型。
每个 NSObject 都隐含了 dataContext 属性。

事件模拟宏

声明事件。第二个可选参数为参数类型。

event_prop(change, MyClass*); 	// @property (nonatomic) MyClass* change;
event_prop(loading, BOOL);		// @property (nonatomic) BOOL loading;
event_prop(complete);			// @property (nonatomic) id complete;

触发事件

event_raise(loading, YES);		// self.loading = YES;
event_raise(complete);			// self.complete = nil;

监听事件

observe(src, loading) { ... }

开始/停止观察

在属性的开/始和停止观察时执行一些代码。对 UI 很有用。请参阅示例。

on_start_observing(ClsName, propName) {
    // <start observing code >
};

on_stop_observing(ClsName, propName) {
    // <stop observing code >
};

示例

响应式样式,依赖于多个数据源

@interface MyModel : NSObject
@property (nonatomic) NSString* name;
@property (nonatomic) NSString* surname;
@property (nonatomic) NSInteger age;
@end

@interface MyViewModel : NSObject
@property (nonatomic) NSString* fullname;
@property (nonatomic) NSString* age;
@end
@implementation MyViewModel
{
    MyModel* _model;
}
- (instancetype)initWithModel:(MyModel*)model {
    self = [super init];
    if (self) {
        _model = model;
        
        bind(model, age) { self.age = [NSString stringWithFormat:@"age: %@ ", @(value)]; };
        
        observe(model, name) { [self check]; };
        observe(model, surname) { [self check]; };
        [self check];
    }
    return self;
}

-(void)check {
    self.fullname = [NSString stringWithFormat:@"%@ %@", _model.name, _model.surname];
}
@end

观察自身属性(少写一些错误代码)

@interface MyView : UIView
@property (nonatomic) NSString* text;
@end

@implementation MyView
-(void)setup {
    UILabel* label = [UILabel new];
    [self addSubview:label];

    observe(self, text) {
        label.text = value;
    };
}
@end

UIButton点击助手

@interface UIButton (ClickHelper)
event_prop(click);
@end
@implementation UIButton (ClickHelper)
+(void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        on_start_observing(UIButton, click) {
            [self addTarget:self action:@selector(_onTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
        };
        on_stop_observing(UIButton, click) {
            if (!inDealloc) {
                [self removeTarget:self action:@selector(_onTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
            }
        };
    });
}
-(void)_touchUpInside {
    event_raise(click);
}
-(id)click { return nil; }
-(void)setClick:(id)click {}
@end

@implementation MyView
-(void)setup {
    UIButton* but = [UIButton new];
    [self addSubview:but];

    observe(but, click) {
        NSLog(@"button click");
    };
}
@end

MVVM

@interface MyViewModel : NSObject
@property (nonatomic) NSString* text;
@end

@implementation MyView
-(void)setup {
    UILabel* label = [UILabel new];
    [self addSubview:label];
    
    bind(MyViewModel, text) {
        label.text = value;
    };
}
@end

@implementation ViewController
-(void)viewDidLoad {
    MyView* view = [MyView new];
    [self.view addSubview:view];
    
    MyViewModel* vm = [MyViewModel new]; // it's not good instatiate view model in view controller, just for example
    vm.text = @"hello";
    view.dataContext = vm;
    
    observe(self.view, tap) {
        vm.text = @"hello, world";
    };
}
@end

许可证

本工程采用MIT许可证 - 详细内容请见LICENSE文件。