Swift可重复赋值的闭包属性

问题

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

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'

相关推荐
songgeb4 天前
YYImage Bug:一行之差,正常 JPEG 如何在 iOS 被读成条纹
ios·swift
fthux4 天前
Tick Rest,一个休息提醒工具的自我修养
macos·开源·swift
Fatbobman(东坡肘子)5 天前
快速消失的硬盘容量 -- 肘子的 Swift 周报 #160
开发语言·人工智能·redis·ios·swiftui·iphone·swift
chaoahyl5 天前
WaterDiary 喝水记录 App 实战(2)· 实现喝水首页与本地保存
swift
茶底世界之下7 天前
iPhone Duo 适配实战,Awaking 踩过的 8 个坑
ios·swiftui·swift
xbzb7 天前
Swift 对象存储建环实验 + Ceilometer 计量:两个收尾服务
swift·ring·实验·ceilometer·象存储·建环
2501_916008897 天前
iOS App 图标、启动屏和多语言搭配 方法,上架前使用KXBuild一次做好 App 图标
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
2501_915918417 天前
iOS 推送通知怎么实现,从 APNs p8 密钥到 iOS 真机收到第一条推送
vscode·ios·objective-c·个人开发·swift·敏捷流程
东坡肘子7 天前
快速消失的硬盘容量 -- 肘子的 Swift 周报 #160
人工智能·swiftui·swift
2501_916007479 天前
Windows OpenSSL 生成 iOS 证书,制作 p12 与描述文件,KXBuild 签名实操
windows·vscode·ios·objective-c·个人开发·swift·敏捷流程