美文网首页
ReactiveCocoa 信号操作

ReactiveCocoa 信号操作

作者: 嗨呀好开心 | 来源:发表于2018-11-06 22:19 被阅读0次

    这篇文章讲解在ReactiveCocoa中关于Signal的一些常用的操作,涉及到信号的转换操作等。
    一.Signal可以通过oberver函数来监听数据流中值的变化

    let (signal, observer) = Signal<String, NoError>.pipe()
    signal.observe { event in
        switch event {
        case let .value(value):  //当Observer 发送一个value事件的时候 执行
            print("Value: \(value)")
        case let .failed(error):  //当Observer 发送一个Error事件的时候 执行
            print("Failed: \(error)")
        case .completed: // 当Observer 发送Completed事件的时候 执行
            print("Completed")
        case .interrupted: // 当Observer 发送Interrupted事件的时候 执行 
            print("Interrupted")
        }
    }
    
    observer.send(value: "1") //print Value: 1 
    observer.sendCompleted() //print  Completed
    observer.send(value: "2") // no print
    这里我们需要注意的是 Completed 和 Interrupted 代表当前事件流的结束。
    

    二.事件流的转换操作,这些操作会将当前事件流转换为一个新的事件流。
    2.1 signal.map操作被用于将一个具有values的事件流 转换为一个新的具有results的事件流。

    let (signal, observer) = Signal<String, NoError>.pipe()
            
    signal
    .map { string in
         return string.uppercased() //在这里一定要return 一个value
    }
    .observeValues { value in print(value) }
            
    observer.send(value: "a")     // Prints A
    observer.send(value: "b")     // Prints B
    observer.send(value: "c")     // Prints C
    //需要注意的是 在map函数中,一定要显示的return一个value,否则signal将无法继续传递,
    //因为它不知道当前value经过map操作转换成了什么。
    

    2.2 signal. filter操作用于过滤某些signal。在某些场景中,可能会产生一些我们不想处理的signal,这时我们可以用filter来过滤掉我们不想处理的signal。就好比过滤掉数组中某些我们不想要的数据。接下来,我们举个例子,用来过滤掉value为奇数的signal的例子

    let (signal, observer) = Signal<Int, NoError>.pipe()
            
    signal
     .filter { number in number % 2 == 0 }
     .observeValues { value in print(value) }
            
     observer.send(value: 1)     // Not printed
     observer.send(value: 2)     // Prints 2
     observer.send(value: 3)     // Not printed
     observer.send(value: 4)     // prints 4
    
    需要注意的是 filter函数必须return一个bool类型的数据,当值为true的时候, signal将会被保留,
    值为false的时候,signal将会被过滤掉。
    

    2.3 signal.reduce函数,用来将事件流中所有的数据结合为一个新的value.注意的是这个最终的value仅仅会在输入流complete的时候被发送, 当然如果事件流interrupted,我们同样不会得到任何结果。例如: 我们对一个int数据流中的所有value进行求和,获得最终的结果

    let (signal, observer) = Signal<Int, NoError>.pipe()
    //reduce需要传递一个初始的结果,和javascript中数组的reduce函数相似
    //例如求和可以给初始值为0, 求积可以初始值为1
    signal
        .reduce(0) { $0 + $1 }
        .observeValues { value in print(value) }
    
    observer.send(value: 1)     // nothing printed
    observer.send(value: 2)     // nothing printed
    observer.send(value: 3)     // nothing printed
    observer.sendCompleted()   // prints 6
    

    2.4 signal.collect() 操作用于聚合事件流中values,转换成为一个array value.同样这个最终的 array value仅仅会在输入流complete的时候被发送。

    let (signal, observer) = Signal<Int, NoError>.pipe()
    
    signal
        .collect()
        .observeValues { value in print(value) }
    
    observer.send(value: 1)     // nothing printed
    observer.send(value: 2)     // nothing printed
    observer.send(value: 3)     // nothing printed
    observer.sendCompleted()   // prints [1, 2, 3]
    

    三:事件流的组合方式(combineLatest, zip, merge, concat, latest)
    3.1 Signal.combineLatest函数,可以用来结合两个或多个事件流的最新值。结合的事件流中,必须每个事件流都发送了一个值,这时Signal.combineLatest函数产生的新的事件流才会有结果输出。在此之后,任何一个结合的事件流有新值发送时,都会导致结合后的事件流有新值输出。当然,在这过程中 如果有siganl被Interrupted,那么结合后的事件流将会立即产生Interrupted响应。

    let (numbersSignal, numbersObserver) = Signal<Int, NoError>.pipe()
    let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
            
    let signal = Signal.combineLatest(numbersSignal, lettersSignal)
    signal.observeValues { next in print("Next: \(next)") }
    signal.observeCompleted { print("Completed") }
            
    numbersObserver.send(value: 0)      // nothing printed
    numbersObserver.send(value: 1)      // nothing printed
    lettersObserver.send(value: "A")    // prints (1, A)
    numbersObserver.send(value: 2)      // prints (2, A)
    numbersObserver.sendCompleted()  // nothing printed
    lettersObserver.send(value: "B")    // prints (2, B)
    lettersObserver.send(value: "C")    // prints (2, C)
    lettersObserver.sendCompleted()  // prints "Completed"
    
    let (numbersSignal, numbersObserver) = Signal<Int, NoError>.pipe()
    let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
            
    let signal = Signal.combineLatest(numbersSignal, lettersSignal)
    signal.observeValues { next in print("Next: \(next)") }
    signal.observeCompleted { print("Completed") }
    signal.observeInterrupted {
           print("Interrupted")
    }
            
    numbersObserver.send(value: 0)      // nothing printed
    numbersObserver.send(value: 1)      // nothing printed
    lettersObserver.send(value: "A")    // prints (1, A)
    numbersObserver.send(value: 2)      // prints (2, A)
    numbersObserver.sendInterrupted()  // prints Interrupted
    lettersObserver.send(value: "B")    // nothing printed
    lettersObserver.send(value: "C")    // nothing printed
    

    3.2 Signal.zip函数将两个或多个事件流的值成对的组合起来,产生一个数据类型为tuple的输出流.那么这个成对我们怎么理解呢?也就是说任何第n个元祖的元素对应的是输入流的第n个元素,这意味着直到每个输入流都发送了第n个值,输出流才会发送第n个值。
    zip函数在实际开发中应用比较多,比如某个页面加载完毕以后,需要同时请求两个数据接口,只有当两个数据接口均响应回数据以后,再去刷新页面。这个时候 我们就可以把每个请求数据接口当做一个输入流事件,然后对这两个输入流事件进行zip,当数据响应回来以后发送value事件,这时通过zip后的事件的observer函数 进行监听,刷新页面。

    let (numbersSignal, numbersObserver) = Signal<Int, NoError>.pipe()
    let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
            
    let signal = Signal.zip(numbersSignal, lettersSignal)
    signal.observeValues { next in print("Next: \(next)") }
    signal.observeCompleted { print("Completed") }
            
    numbersObserver.send(value: 0)      // nothing printed
    numbersObserver.send(value: 1)      // nothing printed
    lettersObserver.send(value: "A")    // prints (0, A)
    numbersObserver.send(value: 2)      // nothing printed
    numbersObserver.sendCompleted()  // nothing printed
    lettersObserver.send(value: "B")    // prints (1, B)
    lettersObserver.send(value: "C")    // prints (2, C) & "Completed"
    // zip后的输出流的Completed事件什么时候执行呢?在我看来,
    //当某个输入流所输入的值都已经被zip后的输出流所发送,
    //然后该输入流发送Completed事件,那么输出流就会触发Completed事件。
    //如果该输入流还有value未被输出流所发送,
    //那么即使输入流发送Completed事件,输出流也无法触发Completed事件。
    //如果大家有不同意见,欢迎评论区提出你的论证。
    

    3.3 signal.flatten操作 用于将多个流碾压为一个单一的流。最终单一的流变成外部流的结果。

    let values = [
    [ 1,    2,      3 ], // siganl 1
       [ 4,      5,     6 ], // siganl 2
             [ 7,     8 ], // // siganl 3
    ]
    
    //以下为3种不同的碾压策略对应的结果
    let merge =
    [ 1, 4, 2, 7,5, 3,8,6 ]
    
    let concat = 
    [ 1,    2,      3,4,      5,     6,7,     8]
    
    let latest =
    [ 1, 4,    7,     8 ]
    

    3.3.1 signal.flatten(.merge),多个事件流的merge操作会将内部事件流的每个值转发给外部事件流。

    let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
    let (numbersSignal, numbersObserver) = Signal<String, NoError>.pipe()
    let (signal, observer) = Signal<Signal<String, NoError>, NoError>.pipe()
           
    //将signal的内部事件流进行合并,
    signal.flatten(.merge).observeValues { print($0) } 
    
    observer.send(value: lettersSignal)
    observer.send(value: numbersSignal) //将lettersSignal,numbersSignal 添加到signal的内部事件流
    //signal则为外部事件流
    observer.sendCompleted()
            
    lettersObserver.send(value: "a")    // prints "a"
    numbersObserver.send(value: "1")    // prints "1"
    lettersObserver.send(value: "b")    // prints "b"
    numbersObserver.send(value: "2")    // prints "2"
    lettersObserver.send(value: "c")    // prints "c"
    numbersObserver.send(value: "3")    // prints "3"
    

    3.3.2signal.flatten(.concat) 操作,用来序列化内部事件流的事件。怎么理解这个策略呢,其实你可以考虑一下javascript中数组的concat操作,它是将两个数组中的数据拼接成一个数组,例如: [1, 2, 3].concat([4,5,6]) = [1,2,3,4,5,6]。所以,signal中的concat同样的道理,将多个内部信号的值拼接成一个单一的数据流,它的拼接策略是当第一个内部信号发送complete以后,外部信号才会接受第二个内部信号发送的值,所以 内部信号的顺序很重要。

    let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
    let (numbersSignal, numbersObserver) = Signal<String, NoError>.pipe()
    let (signal, observer) = Signal<Signal<String, NoError>, NoError>.pipe()
    
    signal.flatten(.concat).observeValues { print($0) }
    observer.send(value: lettersSignal)
    observer.send(value: numbersSignal)
    observer.sendCompleted()
    
    //因为内部信号的顺序为lettersSignal,numbersSignal 所以外部信号先接收lettersSignal发送的值,
    //当lettersSignal发送Completed事件以后,外部信号会依次接收numbersSignal发送的数据
    //所以它和数组的concat函数是不是很相似呢?如果大家有些许怀疑,
    //可以颠倒一下内部信号的顺序看一下打印事件
    numbersObserver.send(value: "1")    // nothing printed
    lettersObserver.send(value: "a")    // prints "a"
    lettersObserver.send(value: "b")    // prints "b"
    numbersObserver.send(value: "2")    // nothing printed
    lettersObserver.send(value: "c")    // prints "c"
    lettersObserver.sendCompleted()     // nothing printed
    numbersObserver.send(value: "3")    // prints "3"
    numbersObserver.sendCompleted()     // nothing printed
    

    3.3.3signal.flatten(. latest) 操作,从最新的事件流转发值。那么怎么理解这个策略呢,当我们向外部事件流中添加多个内部事件流时,多个内部事件流只有最后一个事件流转发值才会被外部事件流接收。

    let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
    let (numbersSignal, numbersObserver) = Signal<String, NoError>.pipe()
    let (signal, observer) = Signal<Signal<String, NoError>, NoError>.pipe()
    
    signal.flatten(.latest).observeValues { print($0) }
    
    observer.send(value: lettersSignal) // nothing printed 
    //此时signal中只有一个lettersSignal内部事件流, 
    //因为numbersSignal此时还不是signal的内部事件流,所以signal不会收到numbersObserver发送的数据
    numbersObserver.send(value: "1")    // nothing printed
    //因为lettersSignal此时为signal中唯一的内部事件流,所以signal会接收到lettersObserver发送的数据
    lettersObserver.send(value: "a")    // prints "a"
    lettersObserver.send(value: "b")    // prints "b"
    numbersObserver.send(value: "2")    // nothing printed
    //此时numbersSignal会将signal中lettersSignal内部事件流给替换掉, 
    //signal只会接收numbersObserver发送的数据,
    //而不会在接收lettersObserver发送的数据. 这就是最新(latest)策略
    observer.send(value: numbersSignal) // nothing printed
    lettersObserver.send(value: "c")    // nothing printed
    numbersObserver.send(value: "3")    // prints "3"
    

    文章内容参考链接: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md。以上内容如有错误或不严谨的地方,欢迎大家在评论区指出问题所在,谢谢!

    相关文章

      网友评论

          本文标题:ReactiveCocoa 信号操作

          本文链接:https://www.haomeiwen.com/subject/fxvexqtx.html