美文网首页iOS 收集swift iOS Developer
使用 Swift 的泛型和闭包实现观察者模式

使用 Swift 的泛型和闭包实现观察者模式

作者: Cyandev | 来源:发表于2016-04-08 22:20 被阅读953次

    我们经常遇到这种情况,就是一个应用中有某些变量可能需要经常被改变,而很多其他模块都需要用到这个变量,我们希望在这个变量被改变时,使用者能够及时知晓并进行更新(执行例如 UI 刷新、重载等操作)。传统一点的做法我们可能会去使用NSNotificationCenter这个类,它确实非常实用,可以轻松做到类似 PubSub 的功能,但是如果有许多变量,我们岂不是要声明许多 Notification?这显然是低效且不现实。

    本文将利用 Swift 的语言特效一步步打造一个十分好用的观察者模式。

    石器时代

    使用泛型封装变量,然后给出一个闭包数组,当变量变化时遍历闭包数组,把新值传过去:

    import Foundation
    
    class UZObservable<T> {
    
        var value: T? {
            set(newValue) {
                self._value = newValue
                self.notify()
            }
            
            get {
                return self._value
            }
        }
        
        typealias Observer = T? -> Void
        
        private var _value: T?
        private var observers = [Observer]()
        
        func notify() {
            self.observers.forEach { $0(self.value) }
        }
        
        func bindObserver(observer: Observer) {
            self.observers.append(observer)
        }
    }
    

    很简单,没什么可解释的。注意一点,闭包需要用 typealias 指派一个类型,不然编译器不认...

    随手撸一个测试界面,我们来看看效果。


    Testing UI

    然后是使用代码:

    override func viewDidLoad() {
            super.viewDidLoad()
    
            // Do any additional setup after loading the view.
            message.bindObserver({ [weak self] (message: String?) in
                self?.label.text = message
            })
    }
    
    Figure 1. Figure 2.

    不出意外的,我们初步实现了观察者模式。

    铁器时代

    但是,但是。随着我们的项目越来越大,一个变量的观察者列表会越来越庞大,然而有许多观察者实际已经被释放了,所以我们必须要有一个方法来处理冗余的观察者。于是,我们可以使用 Dictionary,也就是一个键对应一个观察者闭包,当观察者被释放的时候把相应的键移除掉。

    于是代码变成这样:

    import Foundation
    
    class UZObservable<T> {
    
        var value: T? {
            set(newValue) {
                self._value = newValue
                self.notify()
            }
            
            get {
                return self._value
            }
        }
        
        typealias Observer = T? -> Void
        
        private var _value: T?
        private var observers = [String:Observer]()
        
        func notify() {
            for (_, observer) in self.observers {
                observer(self.value)
            }
        }
        
        func bindObserver(observer: Observer, forKey key: String) {
            self.observers[key] = observer
        }
        
        func removeObserverForKey(key: String) {
            self.observers.removeValueForKey(key)
        }
    }
    

    看起来也蛮简单,但是问题又产生了...字符串化的 key 是十分容易碰撞的,好了,这个方法显然也是不行的。

    信息化时代

    看来我们需要对观察者进行一番修改了,为何不用一个引用对象当做 key 呢?也就是说当一个对象需要观察这个变量,这个对象就把 weakself但做 key 添加到观察者列表中,然后我们定期检测这个对象是否为nil,如果是就把它剔除掉。这显然更加完美,因为我们再不用绞尽脑汁地去想各种奇怪的 key 了。

    实现如下:

    import Foundation
    
    class UZObserver<T> {
        
        var closure: (T? -> Void)!
        weak var reference: AnyObject?
        
    }
    
    class UZObservable<T> {
    
        var value: T? {
            set(newValue) {
                self._value = newValue
                self.notify()
            }
            
            get {
                return self._value
            }
        }
        
        private var _value: T?
        private var observers = [UZObserver<T>]()
        
        func notify() {
            observers.forEach { if $0.reference != nil { $0.closure(self.value) } }
        }
        
        func bindObserver(observer: AnyObject, closure: T? -> Void) {
            self.clean()
            
            let _observer = UZObserver<T>()
            _observer.closure = closure
            _observer.reference = observer
            
            self.observers.append(_observer)
        }
        
        func clean() {
            self.observers = self.observers.filter {
                return $0.reference != nil
            }
        }
    }
    

    这里,每次添加新观察者时都清理一遍无效的观察者,这样就可以保证可观的内存使用。

    上面这段代码十分稳定,大家可以放心地在项目中去使用😄

    相关文章

      网友评论

        本文标题:使用 Swift 的泛型和闭包实现观察者模式

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