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