Swift编程

Swift下UserDefaults的常见使用和注意事项

2019-01-19  本文已影响2人  蛮荒星域

前言

UserDefaults适合存储轻量级的本地客户端数据,这是一种常见的数据持久化方式。(建议:如果是存储大批量的数据不要使用这个方法)

基本用法

Swift2 and above

Store
UserDefaults.standard.set(true, forKey: "Key") //Bool
UserDefaults.standard.set(1, forKey: "Key")  //Integer
UserDefaults.standard.set("TEST", forKey: "Key") //setObject
Retrieve
 UserDefaults.standard.bool(forKey: "Key")
 UserDefaults.standard.integer(forKey: "Key")
 UserDefaults.standard.string(forKey: "Key")
Remove
 UserDefaults.standard.removeObject(forKey: "Key")
Remove all Keys
if let appDomain = Bundle.main.bundleIdentifier {
UserDefaults.standard.removePersistentDomain(forName: appDomain)
 }

Swift2 and below

Store
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "yourkey")
NSUserDefaults.standardUserDefaults().synchronize()
Retrieve
  var returnValue: [NSString]? = NSUserDefaults.standardUserDefaults().objectForKey("yourkey") as? [NSString]

Remove
 NSUserDefaults.standardUserDefaults().removeObjectForKey("yourkey")

其他用法

NSUbiquitousKeyValueStore

这个方法可以基于iCloud做跨设备的UserDefaults数据存储,参考NSUbiquitousKeyValueStore

dictionaryRepresentation

这个方法可以获得当前App存储的所有UserDefaults数据

didChangeNotification

这个通知可以在UserDefault发生改变时发出。可以考虑当这个通知发生时全局进行同步数据。UserDefaults.didChangeNotification

关于Synchronize()

在iOS7或者7以下,一般只会在app返回background的时候才会保存数据到disk,但是iOS8以及以上之后app都会在极其短的周期内去保存数据,除非极其频繁且大规模地进行写入的操作,一般而言都会在可接受的时间内完成这项操作。

在iOS8以及以上,读数据大约需要0.5微妙的时间,但是写入数据需要10倍左右的时间,需要将key-valu通过NSPropertyListSerialization转化成plist data

总而言之,iOS8以及以上的系统内不太建议使用synchronize()方法

比较好的实践-使用UserDefaults的方式

  1. 创建UserDefaults的拓展
  2. 创建枚举值存储需要的key
  3. 保存和提取值
Create extension of UserDefaults
Create enum with required Keys to store in local
Store and retrieve the local data wherever you want

示例

extension UserDefaults{

    //MARK: Check Login
    func setLoggedIn(value: Bool) {
        set(value, forKey: UserDefaultsKeys.isLoggedIn.rawValue)
        //synchronize()
    }

    func isLoggedIn()-> Bool {
        return bool(forKey: UserDefaultsKeys.isLoggedIn.rawValue)
    }

    //MARK: Save User Data
    func setUserID(value: Int){
        set(value, forKey: UserDefaultsKeys.userID.rawValue)
        //synchronize()
    }

    //MARK: Retrieve User Data
    func getUserID() -> Int{
        return integer(forKey: UserDefaultsKeys.userID.rawValue)
    }
}

enum for Keys used to store data

enum UserDefaultsKeys : String {
    case isLoggedIn
    case userID
}

Save in UserDefaults where you want

UserDefaults.standard.setLoggedIn(value: true)          // Bool
UserDefaults.standard.setUserID(value: result.User.id!) // String

Retrieve data anywhere in app

print("ID : \(UserDefaults.standard.getUserID())")
UserDefaults.standard.getUserID()

Remove Values

UserDefaults.standard.removeObject(forKey: UserDefaultsKeys.userID) 

注意事项

UserDefaults 的 integer(forKey:) 回傳 0 的問題

利用UserDefaults我们可以方便地存取一些简单的资料,然而当我们存取的资料类型是Int,Bool,Float,Double时,却会遇到一个特别的问题。因为她们回传的类型不是optional,所以不会返回nil,而是一个预设的值,比如0,false之类。可能我们存在一些需求,希望没有存储值时返回nil,那么有两种方式可以解决这个问题。

这个方法的返回值是不可选的,会有默认值

open func integer(forKey defaultName: String) -> Int
-boolForKey: is equivalent to -objectForKey:, except that it converts the returned value to a BOOL. If the value is an NSNumber, NO will be returned if the value is 0, YES otherwise. If the value is an NSString, values of "YES" or "1" will return YES, and values of "NO", "0", or any other string will return NO. If the value is absent or can't be converted to a BOOL, NO will be returned.

更多参考:

func boolForKey(defaultName: String) -> Bool
func integerForKey(defaultName: String) -> Int
func floatForKey(defaultName: String) -> Float
func doubleForKey(defaultName: String) -> Double
func objectForKey(defaultName: String) -> AnyObject?
func URLForKey(defaultName: String) -> NSURL?
func dataForKey(defaultName: String) -> NSData?
func stringForKey(defaultName: String) -> String?
func stringArrayForKey(defaultName: String) -> [String]?
func arrayForKey(defaultName: String) -> [AnyObject]?
func dictionaryForKey(defaultName: String) -> [String : AnyObject]?

有些是返回可选类型的

方案一

使用register(defaults:)设定找不到key对应的value时回传的预设值,比如nil

let dic = ["isFirstOpenApp": nil]
UserDefaults.standard.register(defaults: dic)

register设定的内容是暂存的,并没有存档,所以每次App启动时都要再设定一次。(并且没有设为nil这种操作,设为nil意味着取消该项的设置,后面取值时依旧会采用默认值)

方案二(可取)

通过回传Any?的object(forKey:)搭配as?转型判断

这个方法返回值是可选的

func object(forKey defaultName: String) -> Any?

比如:

if let number = UserDefaults.standard.object(forKey: "number") as? Int {
   print(number)
} else {
   print("no value")
}

由于回传的类型是Any?,所以找不到key number对应的value时会回传nil

参考链接:UserDefaults预设值

value(forKey:) 和 object(forKey:)

value(forKey:)是KVC的语法,它并不是一个UserDefaults的直接方法。所以最好不要在UserDefaults

Never use value(forKey:) on UserDefaults or Dictionary or any other class unless you have a clearly understood need to use key-value coding to get the desired result.

When you don't have such a need, use the standard access methods provided by UserDefaults object(forKey:)

再补充一点:
The value(forKey:) is not a UserDefaults-only method. It is enabled by the NSKeyValueCoding, which, According to Apple's Documentation.
NSKeyValueCoding is an informal protocol that objects adopt to provide indirect access to their properties. When an object is key-value coding compliant, its properties are addressable via string parameters through a concise, uniform messaging interface.

It happens that UserDefaults is NSKeyValueCoding compliant, so people have started (not necessarily in the correct way) using it for accessing UserDefaults.

简而言之,UserDefaults也是遵循了NSKeyValueCoding协议的,所以使用value(forKey:)也是可以获取到数据,但是不建议这种用法。在UserDefaults里面最好使用object(forKey:),这是标准用法

参考链接:在UserDefaults中object(forKey:)和value(forKey:)的区别

参考

NSUserDefaults — A Swift Introduction
[NSUserDefaults synchronize] is Planned to be Deprecated

以上大多是一些需要注意的问题。关于Swift的常见用法,已经有很多博客在详述了,可以参考:Swift:UserDefaults协议(Swift视角下的泛字符串类型API)

上一篇下一篇

猜你喜欢

热点阅读