iOS 如何优雅地跨层通信

1. 传值 (数据传递)

传值在程序开发中使我们每天需要面临的问题
传值方式: 正向传值,逆向传值,跨层传值...
一般在开发过程中主要研究的是逆向传值和跨层传值

说道传值首先我们先来iOS几个重要的传值方式

1 delegate
逆向传值
一对一
优点: 可以定义多个方法,协议限定让代码更具可读性, 也不容易出错, 使用代理可以让类变得更具有拓展性
缺点: 需要定义协议和定义协议属性, 需要关联对象,被遵循代理对象需要提前知道它的存在 

2 block
正向,逆向
一对一
优点: 使用非常灵活,可以当做方法的参数,方法的返回值,或者是对象属性存在, 不用定义太多的东西, 使用block可以让变得代码更简洁和可观性
缺点: 不像代理那样可以拥有很多方法,多个方法需要定义多个block,同时Block使用不当会造成内存泄漏

3 Notification
正向,逆向,跨层
一对多
优点: 不需要编写多少代码,实现比较简单,对象可以不关联
缺点: 通知需要通过string作为传递key容易出错, 同时通知过多显得太乱,不好调试. 注册通知之后销毁之后需要移除(虽然iOS10以后不移除也不影响)也会有内存泄漏方法.

以上几个经典传值方式各有优缺点,在实际的开发过程中通过上面几个方式确实可以解决我们很多问题,一般使用的是代理和Block来应用在程序中,而通知只作为辅助或者补充方式来解决我们的跨层问题,

通常我们遇到更多的是跨层问题且一对多传值,比如用户登录或退出之后许多界面或者空间需要刷新. 虽然通知可以满足这一要求,但是前面也说过过度使用通知会让代码变得太乱也不好调试.
怎么办?

2. 共享数据(监听)

面对一对多问题我们往往先想到单例,单例拥有一些数据,然后让各个模块监听.这样也是可以的但是使用什么方式监听.
一说到监听往往我们想到的KVO, KVO虽然用法简单, 但是KVO也像通知一样可观性不强,需要通过string来关联,这就需要知道被监听对象的一些属性名,显然一些私有属性我们不能暴露出去,所以KVO看来也是行不通.

通过上述的分析可以渐渐形成一个需求,我们需要一个能像通知一样的订阅和取消订阅,只要订阅了就可以监听到我们想要的变化,同时监听之后能像代理一样有一个协议限定着订阅者.
明确了这一需求,首先就得定义多套协议,和一个数据分发者(单例),订阅者可以通过订阅方式某个协议就能监听到分发者变化. 这里遇到的问题是分发者收到订阅消息怎么保存订阅者,将来怎么找到该订阅者.

3.多代理

这就需要将这些代理管理起来,我们可以定义多个代理集合

// 例如这样:
/// A代理协议
protocol AAADelegate {
}
/// B代理协议
protocol AAADelegate {
}
...

class XXXManager {
     /// 单例
     static let shared: XXXManager = XXXManager()
     
     /// 代理A集合
     private var delegateA: [AAADelegate] = []
     /// 代理B集合
     private var delegateB: [BBBDelegate] = []
     ...
     
     /// 添加A模块代理
     func addDelegateA(_ delegate: AAADelegate) {
        delegateA.append(delegate)
     }
     /// 添加B模块代理
     func addDelegateB(_ delegate: BBBDelegate) {
        delegateA.append(delegate)
     }
     ...
     
     
     ///发送变化消息
     func notiADelegateChange() {
        for delegate in delegateA {
            delegate.xxxxMethod()
        }
     }
     ...
}

// 监听者可以这样订阅

/* XXXObserve_1 同时监听 A B 变化 */
class XXXObserve_1 {
    func init() {
        /// 监听A模块变化
        XXXManager.shared.addDelegateA(self)
        /// 监听B模块变化
        XXXManager.shared.addDelegateB(self)
    }   
}
extension XXXObserve_1: AAADelegate {
    /// 实现代理A
}
extension XXXObserve_1: BBBDelegate {
    /// 实现代理B
}

/* XXXObserve_2 同时监听 B 变化 */
class XXXObserve_2 {
    func init() {
        /// 只监听B模块变化
        XXXManager.shared.addDelegateB(self)
    }
}
extension XXXObserve_1: BBBDelegate {
    /// 实现代理B
}

这样似乎可以办到我们预期的效果. 同时也存在一个问题,当我们想要拓展一个C模块时候, 我们需要再次添加一个属性,和一个添加代理方法

例如:

 /// 代理C集合
 private var delegateC: [CCCDelegate] = []
 /// 添加C代理
 func addDelegateC(_ delegate: CCCDelegate) {}
 ...

显然这样不具有拓展性,但是这个多代理思路看起来是可行的.

4. Hash table(Dictionary)

上面说到每次新加入一个模块都需要定义一个属性和一个方法显然很不合理. 这里就需要合并成一个属性和一个添加方法,我们可以通过hash table建立一个映射,这样就让多代理变得更加动态化,不需要依赖属性.

/// 例如这样:
class XXXManager { 
/// 多代理集合 
/// 每个代理协议都具有一个唯一key值, 比如定义: AAADelegate 可以使用唯一key 'AAADelegate', BBBDelegate 使用唯一key 'BBBDelegate'
private var delegatesCollection: [String: [Any]] = [:]

/// 订阅方法
func addDelegate(_ key: String, observe: Any) {
    if delegatesCollection[key] == nil {
        delegatesCollection[key] = [observe]
    } else {
        delegatesCollection[key].append(observe)
    }
}

 ///发送变化消息
 func notiBDelegateChange() {
   guard let delegateB = delegatesCollection["BBBDelegate"] as? [BBBDelegate] else {
     return
   }
    for delegate in delegateB {
        delegate.xxxxMethod()
    }
 }

这样拓展性上来, 但是遇到像通知一样的问题,这个KEY值是string类型容易出错,而且添加代理方法没有指定协议类型这样很容易出错,忘记实现代理,代码不会有提示了.
感觉饶了一圈又回到了原点,心好累.

5.泛型参数

先举个

/// 交换两个值
// 1. Int类型值交换
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temp = a
    a = b
    b = temp
}
// 2. Double类型值交换
func swapTwoDoubles(_ a: Double, _ b: inout Double) {
    let temp = a
    a = b
    b = temp
}
// 3. String类型值交换
func swapTwoStrings(_ a: inout String, _ b: inout String) {
    let temp = a
    a = b
    b = temp
}
...
 
//泛型代码可以让你写出根据自我需求定义适用于任何类型的,灵活且可重用的函数和类型。它可以让你避免重复代码,用一种清晰和抽象的方式来表达代码的意图
func swapTwoValues(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

通过这个就可大做文章了,我们可以这样

func addDelegate(_ type: T.type, observe: T) {
    // 这样就可以这样定义key值了
    let key = "\(type)" 
    /// 存储同理
    if delegatesCollection[key] == nil {
        delegatesCollection[key] = [observe]
    } else {
        delegatesCollection[key].append(observe)
    }
}

/// 发送消息同理
 func notiBDelegateChange() {
 let key = "\(BBBDelegate.self)"
   guard let delegateB = delegatesCollection[key] as? [BBBDelegate] else {
     return
   }
    for delegate in delegateB {
        delegate.xxxxMethod()
    }
 }

似乎看起来可以了, 满足要求了. 还需要解决一个问题就是需要监听的对象存在数组中是强引用, 这显然是不合理的不像单个代理那样 weak 修饰, 对象之间不会相互依赖.

6.对象包裹

这里将添加待先进性一次weak包裹,再将包裹对象关联到数组中

/// 定义一个包裹对象... 因为是弱引用就必须是一个对象 这里暂时先用 NSObjectProtocol
class WeakProxy where E: NSObjectProtocol{
    /// 弱引用代理
    weak var proxy: E?
    init(proxy: E) {
        self.proxy = proxy
    }
}

// 代理集合就可以这样定义了:
func addDelegate(_ type: T.type, observe: T) where T: NSObjectProtocol {
    // 这样就可以这样定义key值了
    let key = "\(type)" 
    /// 先包裹
    let proxy = WeakProxy(proxy: observe)
    if delegatesCollection[key] == nil {
        delegatesCollection[key] = [proxy]
    } else {
        delegatesCollection[key].append(proxy)
    }
}


/// 发送消息同理
 func notiBDelegateChange() {
 let key = "\(BBBDelegate.self)"
   guard let delegateB = delegatesCollection[key] as? [WeakProxy] else {
     return
   }
    for delegate in delegateB {
        // 取出代理
        delegate.proxy?.xxxxMethod()
    }
 }

这样弱引用问题也解决了,但是还是有一些不足之处,这里使用的HashTable + 动态数组, 关键在于数组开销比较大,每次新添加一个新模块代理需要创建一个数组,内存相对比较浪费, 而且使用数组最大的用途就是通过索引访问元素,而我们这里用到的只有新增,删除,和遍历

7.链表

这里链表最大好处是用多少开辟多少,而且我们这里每次需要包裹一个弱引用对象,我们可以将这个弱引用放在链表节点上,这样更节省空间.

/// 链表 弱引用代理
class WeakProxyLink where E: NSObjectProtocol {
    /// 弱引用代理
    weak var proxy: E?
    
    /// 下一个代理
    var next: WeakProxyLink?
    
    init(proxy: E? = nil, next: WeakProxyLink? = nil) {
        self.proxy = proxy
        self.next = next
    }
}

// 代理集合就可以这样定义了:
func addDelegate(_ type: T.type, observe: T) where T: NSObjectProtocol {
    // 这样就可以这样定义key值了
    let key = "\(type)" 
    /// 先包裹
    
    if delegatesCollection[key] == nil {
        delegatesCollection[key] = WeakProxy(proxy: observe)
    } else {
        if let proxyLink =  delegatesCollection[key] as? WeakProxy {
            /// 
            //获取尾部节点
            let last: WeakProxy = getLastNode(proxyLink)
            /// 追加到尾部
            last.next = WeakProxy(proxy: observe)
        }
     }
}


/// 发送消息同理
 func notiBDelegateChange() {
  let key = "\(BBBDelegate.self)"
  var node = delegatesCollection[key] as? WeakProxyLink    
  /// 遍历链表. 发送消息
    while (node != nil) {
        node?.proxy?.xxxxMethod()
        node = node?.next
    }
 }

8.优化抽取

文采有限直接贴代码
代码地址: https://github.com/install-b/DelegateCenter

结构图:


image

① 线程安全控制:
使用同步队列串行执行

open class SafeExcute {
    private static let queueKey = DispatchSpecificKey()
    private lazy var safeQueue = DispatchQueue.init(label: "\(self)_queue")
    private lazy var queueContext: Int = unsafeBitCast(self, to: Int.self)

    public init() {
        safeQueue.setSpecific(key: Self.queueKey, value: queueContext)
    }
}

public extension SafeExcute {
    func excute(_ block: () throws -> T) rethrows -> T {
        /// 相同队列 直接执行 防止串行执行调用时候递归调用
        if queueContext == DispatchQueue.getSpecific(key: Self.queueKey){ return try block() }
        /// 其他的队列 串行执行
        return try safeQueue.sync(execute: block)
    }
}

② 链表抽取:
职能单一化, 让一个协议对应一个模块拓展


@objc public protocol MultiProxyObjectDelegate {
    /// 代理数量变化监听方法
    func delegateCountDidChange(_ obj: Any, count: Int)
}


/// 组合模式拓展
public protocol MultiProxyObjectExcute {
    associatedtype MPT: NSObjectProtocol
    var proxyObject: MultiProxyObject { get }
}
public extension MultiProxyObjectExcute {
    @discardableResult
    func add(delegate: MPT) -> Bool {
        proxyObject.add(delegate: delegate)
    }
    
    @discardableResult
    func remove(delegate: MPT) -> Bool {
        proxyObject.remove(delegate: delegate)
    }

    func enumerateDelegate(using block:((_ delegate: MPT, _ stop: UnsafeMutablePointer) -> Void)) {
        proxyObject.enumerateDelegate(using: block)
    }
}

/// 多代理对象
open class MultiProxyObject: SafeExcute where T: NSObjectProtocol {
    /// 代理数量 
    public private(set) var proxyCount: Int = 0 {
        didSet {
            if oldValue == proxyCount { return }
            delegate?.delegateCountDidChange(self, count: proxyCount)
        }
    }
    
    /// 代理集合 链表
    private var proxyLink: WeakProxyLink?
    
    /// 代理
    public weak var delegate: MultiProxyObjectDelegate?
}

public extension MultiProxyObject {
    
    /// 添加一个代理
    /// - Parameter delegate: 代理
    @discardableResult
    func add(delegate: T) -> Bool {
        return excute {
            guard proxyLink != nil else {
                proxyLink = WeakProxyLink(proxy: delegate)
                /// 代理数为1
                proxyCount = 1
                return true
            }
            var has = false
            proxyCount = enumrate(addNext: delegate, using: { (proxy) -> Bool in
                if delegate.isEqual(proxy)  {
                    has = true
                    return true
                }
                return false
            })
            return !has
        }
    }
    
    /// 移除一个代理
    /// - Parameter delegate: 从链表中删除一个代理
    @discardableResult
    func remove(delegate: T) -> Bool {
        return excute {
            guard proxyLink != nil else {  proxyCount = 0; return false }
            var result = false
            proxyCount = enumrate(using: { (proxy) -> Bool in
                if delegate.isEqual(proxy)  {
                    result = true
                    return true
                }
                return false
            })
            return result
        }
    }
    
    /// 遍历代理
    /// - Parameter block: 遍历的block
    func enumerateDelegate(using block:((_ delegate: T, _ stop: UnsafeMutablePointer) -> Void)) {
        return excute {
            guard proxyLink != nil else { return }
            
            let stop = UnsafeMutablePointer.allocate(capacity: 1)
            defer {
                stop.deallocate()
            }
            stop.pointee = false
            proxyCount = enumrate(using:  { (proxy) -> Bool in
                if stop.pointee.boolValue == false {
                    block(proxy, stop)
                }
                return false
            })
        }
    }
}

extension MultiProxyObject {
    /// 边遍历边删除销毁的proxy 和 需要删除的proxy
    /// - Parameters:
    ///   - proxyLink: 传入得
    ///   - block: 这个回调是
    ///   - addObj: 最后添加的代理 可以为空即结束遍历不做操作
    private func enumrate(addNext add: T? = nil, using block:((_ delegate: T) -> Bool)) -> Int {
        guard var link = proxyLink else {
            if let addObj = add {
                proxyLink = WeakProxyLink(proxy: addObj)
                return 1
            }

            return  0
        }
        
        var preLink: WeakProxyLink?
        var addObj = add
        func returnValue(_ count: Int) -> Int {
            guard let addObj = addObj else {
                return count
            }
            guard let preLink = preLink else {
                proxyLink = WeakProxyLink(proxy: addObj)
                return 1
            }
            if let next = preLink.next {
                next.next = WeakProxyLink(proxy: addObj)
            } else {
                preLink.next = WeakProxyLink(proxy: addObj)
            }
            return count + 1
        }
        
        var c = 0
        

        func removeCurrentProxy() -> Int? {
            /// 删除无用的代理
            guard let next = link.next else {
                preLink?.next = nil;
                
               if preLink == nil {
                   proxyLink = nil
               }
               return returnValue(c)
            }
            link = next
            if preLink == nil {
                proxyLink = link
            } else {
                preLink?.next = link
            }
            
            return nil
        }
        
        
        
        while true {
             /// 过滤已经释放的代理
            guard let proxy = link.proxy else {
                if let count = removeCurrentProxy() { return returnValue(count) }
                continue
            }
            
            
            /// 这里返回YES 将其删除
            if block(proxy) {
                if addObj != nil {
                    addObj = nil
                    c += 1
                } else {
                    if let count = removeCurrentProxy() {
                        return returnValue(count)
                    }
                }
                
            } else {
                c += 1
            }
            
            preLink = link
            guard let next = link.next else { return returnValue(c) }
            link = next
        }
    }
}

③单例整合
与业务单例分离, 像NotificationCenter那样,不依赖与任何东西

open class DelegateCenter: SafeExcute {
    public static let `default` = DelegateCenter()
    private(set) var delegateCollection: [String: Any] = [String: Any]()
    
    private func lazyGetMultiProxyObject() -> MultiProxyObject where T: NSObjectProtocol {
        excute {
            let name = "\(T.self)"
            if let mProxy = delegateCollection[name] as? MultiProxyObject {
                return mProxy
            }
            let obj = MultiProxyObject()
            delegateCollection[name] = obj
            return obj
        }
    }
}

public extension DelegateCenter {
    func add(_ delegate: T) where T: NSObjectProtocol {
        let object: MultiProxyObject = lazyGetMultiProxyObject()
        object.add(delegate: delegate)
    }
    
    func remove(_ delegate: T) where T: NSObjectProtocol {
        let object: MultiProxyObject = lazyGetMultiProxyObject()
        object.remove(delegate: delegate)
    }


    func enumDelegate(_ : T.Type,
                         using block: (_: T, _: UnsafeMutablePointer) -> Void)
        where T: NSObjectProtocol {
        let object: MultiProxyObject? = excute {  return delegateCollection["\(T.self)"] as? MultiProxyObject }
        object?.enumerateDelegate(using: block)
    }
}

你可能感兴趣的:(iOS 如何优雅地跨层通信)