问题
开发中肯定有遇到过将外界传入的回调闭包保存起来,待内部的某个异步事件完成后,统一回调并清空这个闭包数组的场景,这里有个简单的事例
swift
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)
}
}
}
怎么在不借助任何的数据结构的情况下也能保存外部传入的闭包参数呢?
解决方案
以下是一种解决方案
swift
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
swift
@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
后的事例代码
swift
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'