iOS小技能:MVVM数据绑定的实现方式(KVO、block、Delegate、Notification、RAC)

2022-08-22 11:23:54 浏览数 (1)

前言

MVVM 的实现可以采用KVO进行数据绑定,也可以采用RAC。--- 其实还可以采用block、代理(protocol)实现。

在这里插入图片描述

通信间传递消息的几种方式:block、protocol、通知

I block 与protocol 相比的优点

block 的作用:保存一段代码,到恰当的时候调用,很多时候block是代理的一种优化方案

  1. block比protocol更灵活,更高聚合,低耦合。例如AFN的网络框架中,就可以将“准备请求参数”的代码和“处理后台返回数据”的代码放在一起。
  2. block的灵活还体现在他可以当作方法参数以及返回值。Block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。

在OC中,block是以()的形式去执行的,如果方法返回一个block的话,就可以用()来实现链式编程的效果!

低耦合通信除了block、protocol之外,还有KVO。

1.1 block的内部实现原理及使用

https://blog.csdn.net/z929118967/article/details/74203019

1.2 db快速打印Objective-C方法中block参数的签名

lldb快速打印Objective-C方法中block参数的签名:Python script for lldb that prints an Objective-C block signature

II KVO 与protocol、block相比的优点

实现的方式中,KVO,不需要通过通知中心就可以进行属性值的监听;与bock以及代理相比,可以减少代理大量的方法和block大量的处理逻辑代码。

监听的四种类型

代码语言:javascript复制
typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
    // 提供属性的新值
    NSKeyValueObservingOptionNew = 0x01,
    // 提供属性的旧值
    NSKeyValueObservingOptionOld = 0x02,
    // 如果指定,则在添加观察者的时候立即发送一个通知给观察者,
    // 并且是在注册观察者方法返回之前
    NSKeyValueObservingOptionInitial = 0x04,
    // 如果指定,则在每次修改属性时,会在修改通知被发送之前预先发送一条通知给观察者,
    // 这与-willChangeValueForKey:被触发的时间是相对应的。
    // 这样,在每次修改属性时,实际上是会发送两条通知。
    NSKeyValueObservingOptionPrior = 0x08

2.1 KVO举例(观察者模式)

代码语言:javascript复制

- (void)addObserver:(NSObject *)anObserver
         forKeyPath:(NSString *)keyPath
            options:(NSKeyValueObservingOptions)options
            context:(void *)context
            

  1. anObserver:观察者对象,这个对象必须实现observeValueForKeyPath:ofObject:change:context:方法,以响应属性的修改通知,否则将报错 An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
  2. keyPath:被监听的属性。这个值不能为nil。
  3. options:监听选项,这个值可以是NSKeyValueObservingOptions选项的组合。关于监听选项,我们会在下面介绍。
  4. context:任意的额外数据,我们可以将这些数据作为上下文数据,它会传递给观察者对象的observeValueForKeyPath:ofObject:change:context:方法。这个参数的意义在于用于区分同一对象监听同一属性的多个不同的监听。

监听属性

代码语言:javascript复制
        [tmp addObserver:self forKeyPath:@"URL" options:NSKeyValueObservingOptionNew context:nil];

在dealloc中调用 移除监听

代码语言:javascript复制
- (void)dealloc {
    // 移除监听
    [self.webVIew removeObserver:self forKeyPath:@"xxxx"];
}

属性监听的回调方法

代码语言:javascript复制
/**

keyPath:即被观察的属性,与参数object相关。
object:keyPath所属的对象。
change:这是一个字典,它包含了属性被修改的一些信息。这个字典中包含的值会根据我们在添加观察者时设置的options参数的不同而有所不同。
context:这个值即是添加观察者时提供的上下文信息。

// 属性变化的类型,是一个NSNumber对象,包含NSKeyValueChange枚举相关的值
NSString *const NSKeyValueChangeKindKey;
 
// 属性的新值。当NSKeyValueChangeKindKey是 NSKeyValueChangeSetting,
// 且添加观察的方法设置了NSKeyValueObservingOptionNew时,我们能获取到属性的新值。
// 如果NSKeyValueChangeKindKey是NSKeyValueChangeInsertion或者NSKeyValueChangeReplacement,
// 且指定了NSKeyValueObservingOptionNew时,则我们能获取到一个NSArray对象,包含被插入的对象或
// 用于替换其它对象的对象。
NSString *const NSKeyValueChangeNewKey;
 
// 属性的旧值。当NSKeyValueChangeKindKey是 NSKeyValueChangeSetting,
// 且添加观察的方法设置了NSKeyValueObservingOptionOld时,我们能获取到属性的旧值。
// 如果NSKeyValueChangeKindKey是NSKeyValueChangeRemoval或者NSKeyValueChangeReplacement,
// 且指定了NSKeyValueObservingOptionOld时,则我们能获取到一个NSArray对象,包含被移除的对象或
// 被替换的对象。
NSString *const NSKeyValueChangeOldKey;
 
// 如果NSKeyValueChangeKindKey的值是NSKeyValueChangeInsertion、NSKeyValueChangeRemoval
// 或者NSKeyValueChangeReplacement,则这个key对应的值是一个NSIndexSet对象,
// 包含了被插入、移除或替换的对象的索引
NSString *const NSKeyValueChangeIndexesKey;
 
// 当指定了NSKeyValueObservingOptionPrior选项时,在属性被修改的通知发送前,
// 会先发送一条通知给观察者。我们可以使用NSKeyValueChangeNotificationIsPriorKey
// 来获取到通知是否是预先发送的,如果是,获取到的值总是@(YES)
NSString *const NSKeyValueChangeNotificationIsPriorKey;

——————————————
*/
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{

    if([keyPath isEqualToString:@"transform"]){
        
        CGRect fontBarFrame = self.fontBar.frame;
        fontBarFrame.origin.y = CGRectGetMaxY(self.toolBarView.frame)- KWFontBar_Height - KWEditorBar_Height;
        self.fontBar.frame = fontBarFrame;
        
        
    }else if([keyPath isEqualToString:@"URL"]){
        
        
        NSString *urlString = self.editorView.URL.absoluteString;
        NSLog(@"URL------%@",urlString);
        
        [self handleEvent:urlString];
        
    }else{
        //默认处理
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
    
}


2.2 ReactiveCocoa

RAC 提供了优雅安全的数据绑定。

RAC是函数响应式编程(FRP)框架。ReactiveCocoa结合了几种编程风格:函数式编程(Functional Programming)响应式编程(Reactive Programming)

优雅集中体现在函数式编程,安全体现在响应式编程。

使用RAC解决问题,就不需要考虑调用顺序。每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。

案例:iOS富文本编辑器(基于WKWebview实现,Editor使用WKWebview加载一个本地editor.html文件) https://download.csdn.net/download/u011018979/85675638

III、MVVM的实现总结

在 iOS 的 MVVM 实现中,我们可以使用 RAC 来在 view 和 viewModel 之间充当 binder 的角色,优雅地实现两者之间的信息同步。此外,我们还可以把 RAC 用在 model 层,使用 Signal 来代表异步的数据获取操作,比如读取文件、访问数据库和网络请求等(同样可以在 MVC 的 model 层这么用)。

0 人点赞