美文网首页
Swift 进阶笔记-业务化Tips (3)-帅气的 UserD

Swift 进阶笔记-业务化Tips (3)-帅气的 UserD

作者: wiiale | 来源:发表于2018-01-21 11:20 被阅读0次

    UserDefaults

    UserDefaults 可以存储轻量级的本地客户端数据,适合用于记住用户登录状态、信息等。然而如何写出简单好用不失优雅的存取方式呢?本篇实现一个封装成静态变量的 UserDefaults。先放结果和 Demo:

    // 取
    dataLabel.text = CoolUserDefaults.data.value ?? "nil"
    // 存
    CoolUserDefaults.data.value = changeDataField.text
    

    是否看起来既清爽又简单?

    先上Demo
    https://github.com/wiiale/AdvancedSwiftThinker/tree/master/T03-CoolUserDefaults

    UserDefaults.gif

    写时复制

    写实复制指元素在一般状况下以只读方式共享,资源的复制只有在需要写入的时候才进行。实现写时复制能够在创建保留值语义结构体时保持像指针那样的高效操作。

    使用 Swift 值类型进行写时复制:

    import Foundation
    
    struct User {
        var id = 1
    }
    
    final class Ref<T> {
        var value: T
        init(_ value: T) {
            self.value = value
        }
    }
    
    struct Box<T> {
        private var ref: Ref<T>
        init(_ value: T) {
            ref = Ref(value)
        }
        
        var value: T {
            get { return ref.value }
            set {
                guard isKnownUniquelyReferenced(&ref) else {
                    ref = Ref(newValue)
                    return
                }
                ref.value = newValue
            }
        }
    }
    
    let user = User()
    let box = Box(user)
    var box2 = box     // box、box2共享 box.ref实例
    box2.value.id = 2  // 创建新的box2 ref对象
    

    由于 struct 是一个值类型,当我们将它赋值给另一个变量时,它的值被复制,而属性 ref 遗留的实例被两个副本共享,因为它是一个引用类型。

    guard isKnownUniquelyReferenced(&ref) else {
        ref = Ref(newValue)
        return
    }
    

    这个方法中检查引用是否共享一个引用实例。

    封装 UserDefaults

    但是本篇文章的重点并不在于写时复制,而在于实现写实复制时传递的思想,封装起来也并不繁琐复杂。

    final public class UserDefaultsBox<T> {
        
        public var value: T {
            didSet {
                setterAction(value)
            }
        }
        
        public typealias SetterAction = (T) -> Void
        var setterAction: SetterAction
        
        public init(_ v: T, setterAction action: @escaping SetterAction) {
            value = v
            setterAction = action
        }
    }
    

    关联 UserDefaults 的存取功能封装 Box。

    public var value: T {
        didSet {
            setterAction(value)
        }
    }
    

    value 的实现与写时复制在需要时复制一样,只有在需要被赋值时才进行 setter 操作。

    实现需要存储的内容:

    private let dataKey = "userData"
    ···
    
    final public class CoolUserDefaults {
        
        static let defaults = UserDefaults(suiteName: "cool.user.defaults")!
        
        /// User Data
        public static var data: UserDefaultsBox<String?> = {
            let data = defaults.string(forKey: dataKey)
            
            return UserDefaultsBox<String?>(data) { data in
                defaults.set(data, forKey: dataKey)
            }
        }()
    
        ···
    }
    

    接着我们的静态变量是一个 Box 对象,将 defaults.set 设置为 set 方法。从而我们实现了以静态变量作为表现形式的 UserDefaults。

    使用

    // 取(查)
    dataLabel.text = CoolUserDefaults.data.value ?? "nil"
    // 存(增、改)
    CoolUserDefaults.data.value = changeDataField.text
    // 清除(删)
    CoolUserDefaults.data.value = nil
    

    批量清理

    最后可以在自定义 CoolUserDefaults 中实现批量清缓存的方法,以便用于像退出登录一样需要清空用户数据的操作。

    final public class CoolUserDefaults {
    
        ···
    
        public class func cleanAllUserDefaults() {
            
            do {
                data.value = nil
                // others ···
            }
            
            // reset suite
            
            let dict = defaults.dictionaryRepresentation()
            dict.keys.forEach({
                defaults.removeObject(forKey: $0)
            })
            defaults.synchronize()
            
            // reset standardUserDefaults
            
            let standardUserDefaults = UserDefaults.standard
            standardUserDefaults.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
            standardUserDefaults.synchronize()
        }
    }
    

    使用时直接调用CoolUserDefaults. cleanAllUserDefaults()即可。

    参考资料:
    Use Copy-On-Write With Swift Value Types
    YepUserDefaults

    文章Demo 汇总
    https://github.com/wiiale/AdvancedSwiftThinker

    本册文集中以“提出简单需求->简单实现需求片段”为流程,内容只针对该知识点实现的业务实例进行熟悉,业务也必定存在比文章方法更好的实现方式,文章旨在分析知识点并加深理解。文集不普及基本知识,不包含《Swift 进阶》书籍的详细内容。深入学习Swift请大家支持正版书籍(ObjC 中国)

    相关文章

      网友评论

          本文标题:Swift 进阶笔记-业务化Tips (3)-帅气的 UserD

          本文链接:https://www.haomeiwen.com/subject/zglapxtx.html