Skip to content

Instantly share code, notes, and snippets.

@heronlyj
Last active May 5, 2018 07:23
Show Gist options
  • Save heronlyj/db9e82523d318b9a00a5cc66efe08183 to your computer and use it in GitHub Desktop.
Save heronlyj/db9e82523d318b9a00a5cc66efe08183 to your computer and use it in GitHub Desktop.
一个轻量的数据监听类
//
// Listener.swift
// BMKP
//
// Created by lyj on 19/01/2017.
// Copyright © 2017 bmkp. All rights reserved.
//
import Foundation
public struct ListenserKey {
fileprivate let obj: String
public init(obj: String) {
self.obj = obj
}
public func createrKey(_ v: String) -> String {
return obj + "." + v + ".listenerKey"
}
}
extension NSObject {
public var listenKey: ListenserKey {
return ListenserKey(obj: "\(hashValue)")
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// listener name and action
public struct Listener<T>: Hashable {
public typealias Action = (T) -> Void
public let key: String
public let action: Action
public var hashValue: Int {
return key.hashValue
}
}
public func ==<T>(lhs: Listener<T>, rhs: Listener<T>) -> Bool {
return lhs.key == rhs.key
}
/// 被观察对象 T 为 value 类型
public class ListenAble<T> {
public typealias Change = (new: T, old: T)
public typealias GuardAction = (Change) -> Bool
public typealias SetterAction = (T) -> Void
/// value 为最新的值,finish 闭包是终止观察的,调用 finish() 则会 remove
public typealias OptionalEscapeingAction = (_ value: T, _ finish: @escaping () -> Void) -> Void
/// 最新的 value
public private(set) var value: T {
didSet {
setterAction(value)
listenerSet.forEach {
$0.action(value)
}
}
}
/// 唯一可以更新 value 的方法 需要通过 updateGuard 来判断,只有返回 true 才能更新
///
/// - Parameter v: 需要更新的 value
public func updateValue(_ v: T) {
guard updateGuard((new: v, old: value)) else { return }
value = v
}
var updateGuard: GuardAction
var setterAction: SetterAction
var listenerSet = Set<Listener<T>>()
/// 是否没有监听了
public var emptyListen: Bool {
return listenerSet.isEmpty
}
/// 初始化
///
/// - Parameters:
/// - v: 给一个初始值
/// - updateGuard: 设置一个 guard 用来判断是否能够更新
/// - setter: 初始化绑定的更新回调
public init(v: T, updateGuard: GuardAction? = nil, setter: @escaping SetterAction = { _ in }) {
value = v
setterAction = setter
self.updateGuard = updateGuard ?? { _ in return true }
}
/// 绑定观察者
///
/// - Parameters:
/// - key: 唯一标示
/// - count: 更新触发的次数,nil 则没有限制
/// - action: 观察者
public func bindListener(key: String, count: Int? = nil, action: @escaping Listener<T>.Action) {
guard let count = count, count > 0 else {
listenerSet.insert(Listener<T>(key: key, action: action))
return
}
let counter = createCounter(start: count)
listenerSet.insert(Listener<T>(key: key, action: { [weak self] t in
action(t)
// 每次触发更新,如果 count == 0 则 remove, counter 会自减
if counter() == 0 {
self?.removeListener(key: key)
}
}))
}
/// 绑定观察者并且立即触发一次回调
///
/// - Parameters:
/// - key: 唯一标示
/// - count: 更新触发的次数, nil 则没有限制,立即触发的一次回调不回改变 count
/// - action: 观察者
public func bindAndFireListener(key: String, count: Int? = nil, action: @escaping Listener<T>.Action) {
bindListener(key: key, count: count, action: action)
action(value)
}
/// 只观察下一次的 value 更新,无论是什么值,回调一次就 remove
///
/// - Parameters:
/// - action: 观察者
public func fireOnce(_ action: @escaping Listener<T>.Action) {
bindListener(key: UUID().uuidString, count: 1, action: action)
}
/// OptionalEscapeingAction 闭包与 Listener<T>.Action 的区别是多了一个 finish 参数, 这同样是一个闭包, 它的作用是 removeListener
/// 调用时机自己控制,设计的使用场景是,需要满足一定条件的值,一旦得到这个值之后就不需要监听了,此时执行 finish 然后 remove
///
/// - Parameter action: 更新回调闭包
public func fireUntilCompleted(_ action: @escaping OptionalEscapeingAction) {
let key = UUID().uuidString
// 只有 finish 回调一次就 remove finish 的出发由注册的地方来实现
let finish = {
self.removeListener(key: key)
}
self.bindListener(key: key) {
action($0, finish)
}
action(value, finish)
}
/// 移除观察者
///
/// - Parameter key: 唯一标识
public func removeListener(key: String) {
if let index = listenerSet.index(where: { $0.key == key }) {
listenerSet.remove(at: index)
}
}
/// 移除所有
public func removeAll() {
listenerSet.removeAll(keepingCapacity: false)
}
/// 创建一个计时器,每调用一次计数都会减一
fileprivate func createCounter(start: Int) -> () -> Int {
var startCount = start
return {
startCount -= 1
return startCount
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment