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

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 中国)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容