RxSwift 6 Filter Operator 实操

1 优化现有应用

前面一章引入了 RxSwift 中的函数式编程的方面, 即 Operator.

下面就来使用之前学过的一些过滤型操作符, 看看在实际工程中如何使用它们.

当然下面的这些代码只是引路作用, 并非是进入所谓的 "操作符最佳实践". 先入门, 然后再慢慢提高优化!

2 共享 Observable 序列, 实现一些过滤操作

先来看一个情况:

let numbers = Observable<Int>.create { observer in
    let start = getStartNumber()
    observer.onNext(start)
    observer.onNext(start+1)
    observer.onNext(start+2)
    observer.onCompleted()
    return Disposables.create()
}

getStartNumber() 方法是这样的:

var start = 0
func getStartNumber() -> Int {
start += 1
    return start
}

下面对该序列进行观察.

numbers
  .subscribe(onNext: { el in
    print("element [\(el)]")
  }, onCompleted: {
    print("-------------")
  })

如果多次观察这段代码, 最后的输出会在每次观察的时候都不一样.

这是为什么呢?

因为每次观察者开始观察的时候, 都会去执行 Create 块中的代码, 从而导致序列的变化.

有时这样的变化是有意为之, 但有时却不希望发生.

如果不希望每次观察都是去重新执行 create 块, 则可以使用 share() 操作符.

let newPhotos = photosViewController.selectedPhotos
  .share()

这样的话, 任何在 newPhotos 上的新观察者得到的都是同样的可观察序列.

实际上的原理是这样的: 当序列的观察者数量从 0 变化到 1 的时候, share 会去创建一个 Observable 序列出来. 当观察者再增加时, share 则会直接使用已创建的序列交给观察者. 如果在该序列上的所有观察者都被释放了. 则 share 会将创建出来的序列销毁. 而如果后面又有新的观察者, 则又会重复上述过程.

另外 share 操作符有一个特性, 即它不会提供在观察者开始观察之前序列已发射的内容. 如果需要 share 的共享特性, 又需要知道最后一个发射的内容, 则可以使用 shareReply(_) 操作符, 它可以指定一个缓存, 缓存之前发射过的若干事件.

2.1 ignoreElments 操作符

这个操作符的作用是只允许 complete 或 error 通过. 这样的话, 就可以用来观察完成或是错误. 当然这里只是一个假设的使用情况, 不对 next 事件作出响应也是一个结果.

2.2 实现过滤掉相同图片的功能

另外如果想添加照片的时候只添加不同的文件名的图片, 则也可以使用过滤.

但是多个 UIImage 对象间的区别不能通过地址来识别, 也无法通过名字或 URL 识别.(除非是自定义的子类. 实际使用的时候就可以继承并在子类中添加类似属性用于识别不同的 Image.)

不过在本例中仅使用字节长度作为判断依据, 防止跑题.

newPhoto
    .filter({ newImage in
        // 过滤竖向的图片.
        newImage.size.width > newImage.size.height
    })
    .filter({ [weak self] newImage in
        // 过滤相同大小的图片, 防止重复选择
        let len = UIImagePNGRepresentation(newImage)?.count ?? 0
        guard self?.imageCache.contains(len) == false else { return false }
        self?.imageCache.append(len)
        return true
    })
    .subscribe(onNext: { [weak self] newImage in
        guard let images = self?.images else { return }
        images.value.append(newImage)
        }, onDisposed: {
            print("completed photo selection")
    })
    .addDisposableTo(photosViewController.bag)

2.3 实现当只有满足条件时才会允许 next 消息通过

这里需要使用 takeWhile 操作符. 可以为 takeWhile 提供一个布尔判断, 如果值变为假之后, 就可以取消掉之后的所有元素.

newPhoto
    .takeWhile({ [weak self] _ in
        return (self?.images.value.count ?? 0) < 6
    })
    // ... 下面还是之前的过滤代码等内容

这样一来, 条件为 false 的时候, 就不会允许 next 通过了.

3 优化照片选择器

下面首先来构造一个自定义 observable, 然后通过不同的过滤器来操作它, 提升用户体验:

这个 observable 是针对某个权限的请求和允许情况的:

import Foundation
import Photos
import RxSwift

extension PHPhotoLibrary {
    static var authorized: Observable<Bool> {
        return Observable.create({ observer in
            DispatchQueue.main.async {
                if authorizationStatus() == .authorized {
                    observer.onNext(true)
                    observer.onCompleted()
                } else {
                    observer.onNext(false)
                    requestAuthorization({ status in
                        observer.onNext(status == .authorized)
                        observer.onCompleted()
                    })
                }
            }
            return Disposables.create()
        })
    }
}

上面的代码中, 当外界有新的观察者开始观察的时候, 都会触发 create 方法的执行. 所以每次都会去判断权限.

使用 DispatchQueue.main.async 的意思是: 首先 DispatchQueue.main 表示在主线程中执行, 而 DispatchQueue.global() 是在后台线程中执行, 且 sync 表示当前线程会等待该工作块的执行完毕后再继续执行, 而 async 的话, 当前线程不会等待该工作块的执行完毕.

sync 方式执行的工作块, 当前线程会等待该工作块执行完毕后再继续执行. 而 async 方式执行的工作块当前线程是不会等待它结束的.

3.1 开始外界的观察操作

下面就在外界开始观察, 若权限是允许的情况下, 则重新加载照片.

由于权限的请求只能一次, 故当前状态有两种:

  • 用户第一次运行程序, 第一次请求权限, 且点击的是 ok.

    false---true---completed

  • 之后的运行过程, 如果之前允许过该权限.

    true---completed

经过分析, 我们知道要重新加载照片的前提就是遇到 true, 而 true 的事件肯定是最后一个 next 事件.

下面就开始观察:

let authorized = PHPhotoLibrary.authorized.share()
authorized.skipWhile({ elem in
    elem == false
}).take(1).subscribe(onNext: { [weak self] _ in
    self?.photos = PhotosViewController.loadPhotos()
    DispatchQueue.main.async {
        self?.collectionView?.reloadData()
    }
}).addDisposableTo(bag)

不过在 RxSwift 中尽可能不要使用 GCD 来切换线程, 更多地是使用 scheduler 来达到目的. 详见 15章.

3.2 当用户不允许时显示错误信息

每次这样的情况都需要先分析好当前的 Observable 中的事件序列是个什么样的情况.

4 利用时间的操作符

略看.

第六章结束.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,649评论 18 139
  • 本篇文章介主要绍RxJava中操作符是以函数作为基本单位,与响应式编程作为结合使用的,对什么是操作、操作符都有哪些...
    嘎啦果安卓兽阅读 2,853评论 0 10
  • 参考:给 Android 开发者的 RxJava 详解-扔物线深入浅出RxJava 基础 "a library f...
    Vincen1024阅读 542评论 0 1
  • 最近在学习RxSwift相关的内容,在这里记录一些基本的知识点,以便今后查阅。 Observable 在RxSwi...
    L_Zephyr阅读 1,750评论 1 4
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,035评论 25 707