问题

开发中肯定有遇到过将外界传入的回调闭包保存起来,待内部的某个异步事情完成后,一致回调并清空这个闭包数组的场景,这里有个简单的事例

class Vehicle {}
enum VehicleManager {
  private static var loadClosures: [([Vehicle], Bool) -> Void] = [] 
  private static var isLoading = false
  static func loadList(_ completion: (([Vehicle], Bool) -> Void)? = nil) {
    if isLoading {
      loadClosures.appendIfNonNil(completion)
      return
    } 
    isLoading = true 
    loadClosures.appendIfNonNil(completion)
    AIPManger.loadVehicle { res in
      switch res {
      case .success(let models):
        onDidFetch(models, true)
      case .failure: 
        onDidFetch([], false)
      }
    }
  }
  private static func onDidFetch(_ models: [Vehicle], _ success: Bool) {
    isLoading = false
    let closures = loadClosures 
    loadClosures = [] 
    for c in closures { 
      c(models, success) 
    } 
  } 
}

怎样在不借助任何的数据结构的情况下也能保存外部传入的闭包参数呢?

处理方案

以下是一种处理方案

class Vehicle {}
enum VehicleManager {
  private static var loadClosure: (([Vehicle], Bool) -> Void)?
  private static var isLoading = false
  private static func stash(_ completion: (([Vehicle], Bool) -> Void)?) {
    guard let cmp = completion else { return }
    if let oldVal = loadClosure {
      loadClosure = { arg1, arg2 in
        oldVal(arg1, arg2)
        cmp(arg1, arg2)
      }
    } else {
      loadClosure = cmp
    }
  }
  static func loadList(_ completion: (([Vehicle], Bool) -> Void)? = nil) {
    stash(completion)
    if isLoading { return }
    isLoading = true
    AIPManger.loadVehicle { res in
            isLoading = false
      let closure = loadClosure
      loadClosure = nil
      switch res {
      case .success(let models):
        closure?(models, true)
      case .failure:
        closure?([], false)
      }
    }
  }
}

仔细观察会发现stash办法是对loadClosure特点的一层包装,而这恰好是PropertyWrapper所要处理的,不太了解PropertyWrapper能够移步Swift 最佳实践之 Property Wrapper

@propertyWrapper
public struct Param2Closure<T, U> {
    public typealias Closure = (T, U) -> Void
    private var closure: Closure?
    public init() {}
    public mutating func reset() {
        closure = nil
    }
    public mutating func callAndReset(_ arg1: T, _ arg2: U) {
        closure?(arg1, arg2)
        closure = nil
    }
    public var wrappedValue: Closure? {
        get { closure }
        set {
            guard let val = newValue else { return }
            if let oldVal = closure {
                closure = { arg1, arg2 in
                    oldVal(arg1, arg2)
                    val(arg1, arg2)
                }
            } else {
                closure = val
            }
        }
    }
}

运用该PropertyWrapper后的事例代码

class Vehicle {}
enum VehicleManager {
    @Param2Closure
    private static var loadClosure: (([Vehicle], Bool) -> Void)?
    private static var isLoading = false
    static func loadList(_ completion: (([Vehicle], Bool) -> Void)? = nil) {
        loadClosure = completion
        if isLoading { return }
        isLoading = true
        AIPManger.loadVehicle { res in
            switch res {
            case .success(let models):
                _loadClosure.callAndReset(models, true)
            case .failure:
                _loadClosure.callAndReset(models, false)
            }
            isLoading = false
        }
    }
}

简洁明了了许多,后续有类似需求,一个PropertyWrapper就能够搞定。

后话

Param2Closure特点包装器已收录在自己编写的SwifterKnife傍边,一起还包含只要一个参数Param1Closure和无参数VoidClosure版别的。

运用Swift开发有两年多时间,该库积累了许多自己在开发傍边常用的办法、扩展、东西类、常见问题的处理方案,致力于写出更高效更契合swift风格的代码,日常维护更新是在develop分支,不定期合并到master分支,欢迎读者提出宝贵意见和主张

小小Tips

若有意运用该库,主张

pod 'SwifterKnife', :git => 'https://github.com/CoderLouie/SwifterKnife.git', :branch => 'develop'