美文网首页
SwiftyUserDefaults源码阅读

SwiftyUserDefaults源码阅读

作者: meowboss | 来源:发表于2017-08-23 03:28 被阅读233次

简介:

关于SwiftyUserDefaults的使用详情可看项目的GitHub地址,本文不再阐述,OK,let's begin!

Proxy:

该类是UserDefaults 拓展中的嵌套类。

class Proxy {
    fileprivate let defaults: UserDefaults
    fileprivate let key: String
    
    fileprivate init(_ defaults: UserDefaults, _ key: String) {
        self.defaults = defaults
        self.key = key
    }
}

该类有两个存储属性和一个初始化方法,都是私有不对外公开的。
除此之外,该类还有一大堆可选和非可选的计算型属性。

public var string: String? {
            return defaults.string(forKey: key)
        }
 public var stringValue: String {
            return string ?? ""
        }

两者的本质都是用defaults属性根据返回值的类型调用相应的取值方法,区别是非可选的属性名是以类型加value结尾的,并且会在可选计算属性值为nil的时候返回一个默认值。另外一点值得注意的是,该类还number拆分为int,double和bool,非常实用,可能你会觉得奇怪为什么该类的计算属性都是只读的,没关系下面会见到的。

UserDefaults拓展:

同样,在UserDefaults的拓展中,也对NSNumber类型的取值开了额外的方法,不过,这里没有细分为对应的int,double和bool。

 func numberForKey(_ key: String) -> NSNumber? {
        return object(forKey: key) as? NSNumber
    }
下标:
public subscript(key: String) -> Proxy {
        return Proxy(self, key)
    }

这里上面的Proxy类就被用到了,通过访问UserDefaults实例下标key我们将返回一个Proxy对象,该对象里面存储了UserDefaults实例本身和参数key,将key和UserDefault实例一一绑定。这里主要还是为了获取key,有了key我们可以通过Proxy的实例方法根据不同类型的值获取相应的计算属性的值。可能这里你又有疑惑了,我怎么知道我该调用stringValue还是arrayValue,要记住每次存了什么类型的值得多操心啊,没事别急,后面会讲到。

public subscript(key: String) -> Any? {
        get {
            // return untyped Proxy
            // (make sure we don't fall into infinite loop)
            let proxy: Proxy = self[key]
            return proxy
        }
        set {
            
            guard let newValue = newValue else {
                removeObject(forKey: key)
                return
            }
            
            switch newValue {
            // @warning This should always be on top of Int because a cast
            // from Double to Int will always succeed.
            case let v as Double: self.set(v, forKey: key)
            case let v as Int: self.set(v, forKey: key)
            case let v as Bool: self.set(v, forKey: key)
            case let v as URL: self.set(v, forKey: key)
            default: self.set(newValue, forKey: key)
            }
        }
    }

接着又对下标进行了重载,将返回类型限定为Proxy,同时在set的时候做了真正的业务操作,将值进行保存。这是一个很巧妙的做法,通过调用UserDefaults实例的下标,我们设置在value的时候完成了真的的存储,而在想要取value值的时候我们可以根据value的类型取Proxy实例相应的计算属性。至此Proxy类的作用已经很明显了,为我们的UserDefaults做代理,根据想要的类型取出相应的值,避免了swift啰嗦的可选与类型转换处理。

判断是否有值:
public func hasKey(_ key: String) -> Bool {
        return object(forKey: key) != nil
    }
移除值:
public func remove(_ key: String) {
        removeObject(forKey: key)
    }
 public func removeAll() {
        for (key, _) in dictionaryRepresentation() {
            removeObject(forKey: key)
        }
    }
全局常量:
public let Defaults = UserDefaults.standard

这里定义了一个全局常量,我们可以在项目中新建一个.swift文件,倒入SwiftyUserDefaults框架,在文件中定义一个同名常量:

let Defaults = SwiftyUserDefaults.Defaults

这样我们就可以尽情使用SwiftyUserDefaults框架了,而不是每次要在使用的文件都倒入一遍SwiftyUserDefaults框架。

DefaultsKeys:

open class DefaultsKeys {
    fileprivate init() {}
}

该类的作用差不多就是一个充当一个容器,从命名就可以看出来,通过extension添加静态key的方式集中管理key。

####DefaultsKey:
open class DefaultsKey<ValueType>: DefaultsKeys {
    // TODO: Can we use protocols to ensure ValueType is a compatible type?
    public let _key: String
    
    public init(_ key: String) {
        self._key = key
        super.init()
    }
}

剧透下,该类该框架的核心类,用来定义各种类型的key和相应的value的,你的疑惑终于要解开了。
该类是一个泛型类,用来定义key与相应的ValueType,这个时候泛型的优势就体现出来了。
我们知道,我们之前都是通过下标进行value的存取的,不过key的类型都是String,这里为了支持该类型的key,我们需要添加相应的方法支持。

自定义类型key的value的设置:

extension UserDefaults {
    /// This function allows you to create your own custom Defaults subscript. Example: [Int: String]
    public func set<T>(_ key: DefaultsKey<T>, _ value: Any?) {
        self[key._key] = value
    }
}

本质还是通过下标,用DefaultsKey的key来设置value的。

自定义类型key的value的判断和移除:

 public func hasKey<T>(_ key: DefaultsKey<T>) -> Bool {
        return object(forKey: key._key) != nil
    }

判断是否有值。

 public func remove<T>(_ key: DefaultsKey<T>) {
        removeObject(forKey: key._key)
    }

移除该key。

下标支持自定义类型key:

extension UserDefaults {
    public subscript(key: DefaultsKey<String?>) -> String? {
        get { return string(forKey: key._key) }
        set { set(key, newValue) }
    }
 public subscript(key: DefaultsKey<String>) -> String {
        get { return string(forKey: key._key) ?? "" }
        set { set(key, newValue) }
    }
}

好吧,上一步的set方法就是为这里的下标赋值做铺垫的。这里的下标取值直接用了UserDefaults实例自己的方法取的。为啥?这部很明显么,都知道ValueType类型了,还用Proxy干啥?
以上都是常见的ValueType,如果想支持别的支持NSCoding的ValueType,重载该方法就可以了,不过需要调用archive和unarchive。

 public func unarchive<T>(_ key: DefaultsKey<T>) -> T? {
        return data(forKey: key._key).flatMap { NSKeyedUnarchiver.unarchiveObject(with: $0) } as? T
    }
    
    public func unarchive<T>(_ key: DefaultsKey<T?>) -> T? {
        return data(forKey: key._key).flatMap { NSKeyedUnarchiver.unarchiveObject(with: $0) } as? T
    }

实现:

let Defaults = SwiftyUserDefaults.Defaults
// userInfo
extension DefaultsKeys {
    static let name = DefaultsKey< String?>("name")
    static let age = DefaultsKey<Int?>("age")
    static let sex = DefaultsKey< Sex?>("sex")
}
enum Sex {
    case man,women
}
extension UserDefaults {
    subscript(key: DefaultsKey< Sex?>) -> Sex? {
        get { return unarchive(key) }
        set { archive(key, newValue) }
    }
}

示例:

let user = User(name:"meow",age: 1,sex: .man)
Defaults[. name] = user.name
Defaults[. age] = user.age
Defaults[. sex] = user. age

相关文章

网友评论

      本文标题:SwiftyUserDefaults源码阅读

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