前言
UserDefaults
適合存儲(chǔ)輕量級(jí)的本地客戶端數(shù)據(jù)从藤,這是一種常見的數(shù)據(jù)持久化方式菌羽。(建議:如果是存儲(chǔ)大批量的數(shù)據(jù)不要使用這個(gè)方法)
基本用法
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
這個(gè)方法可以基于iCloud做跨設(shè)備的UserDefaults數(shù)據(jù)存儲(chǔ),參考NSUbiquitousKeyValueStore
dictionaryRepresentation
這個(gè)方法可以獲得當(dāng)前App存儲(chǔ)的所有UserDefaults數(shù)據(jù)
didChangeNotification
這個(gè)通知可以在UserDefault發(fā)生改變時(shí)發(fā)出编丘∑溽悖可以考慮當(dāng)這個(gè)通知發(fā)生時(shí)全局進(jìn)行同步數(shù)據(jù)丰泊。UserDefaults.didChangeNotification
關(guān)于Synchronize()
在iOS7或者7以下,一般只會(huì)在app返回background的時(shí)候才會(huì)保存數(shù)據(jù)到disk隙轻,但是iOS8以及以上之后app都會(huì)在極其短的周期內(nèi)去保存數(shù)據(jù)埠帕,除非極其頻繁且大規(guī)模地進(jìn)行寫入的操作,一般而言都會(huì)在可接受的時(shí)間內(nèi)完成這項(xiàng)操作玖绿。
在iOS8以及以上敛瓷,讀數(shù)據(jù)大約需要0.5微妙的時(shí)間,但是寫入數(shù)據(jù)需要10倍左右的時(shí)間斑匪,需要將key-valu通過NSPropertyListSerialization
轉(zhuǎn)化成plist data
總而言之呐籽,iOS8以及以上的系統(tǒng)內(nèi)不太建議使用synchronize()
方法
比較好的實(shí)踐-使用UserDefaults的方式
- 創(chuàng)建UserDefaults的拓展
- 創(chuàng)建枚舉值存儲(chǔ)需要的key
- 保存和提取值
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)
注意事項(xiàng)
UserDefaults 的 integer(forKey:) 回傳 0 的問題
利用UserDefaults我們可以方便地存取一些簡單的資料,然而當(dāng)我們存取的資料類型是Int,Bool狡蝶,F(xiàn)loat宙刘,Double時(shí),卻會(huì)遇到一個(gè)特別的問題牢酵。因?yàn)樗齻兓貍鞯念愋筒皇莖ptional悬包,所以不會(huì)返回nil,而是一個(gè)預(yù)設(shè)的值馍乙,比如0布近,false之類∷扛瘢可能我們存在一些需求撑瞧,希望沒有存儲(chǔ)值時(shí)返回nil,那么有兩種方式可以解決這個(gè)問題显蝌。
這個(gè)方法的返回值是不可選的预伺,會(huì)有默認(rèn)值
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:)設(shè)定找不到key對(duì)應(yīng)的value時(shí)回傳的預(yù)設(shè)值,比如nil
let dic = ["isFirstOpenApp": nil]
UserDefaults.standard.register(defaults: dic)
register設(shè)定的內(nèi)容是暫存的曼尊,并沒有存檔酬诀,所以每次App啟動(dòng)時(shí)都要再設(shè)定一次。(并且沒有設(shè)為nil這種操作骆撇,設(shè)為nil意味著取消該項(xiàng)的設(shè)置瞒御,后面取值時(shí)依舊會(huì)采用默認(rèn)值)
方案二(可取)
通過回傳Any?的object(forKey:)
搭配as?
轉(zhuǎn)型判斷
這個(gè)方法返回值是可選的
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對(duì)應(yīng)的value時(shí)會(huì)回傳nil
value(forKey:) 和 object(forKey:)
value(forKey:)
是KVC的語法肴裙,它并不是一個(gè)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:)
再補(bǔ)充一點(diǎn):
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
協(xié)議的蜻懦,所以使用value(forKey:)
也是可以獲取到數(shù)據(jù),但是不建議這種用法夕晓。在UserDefaults
里面最好使用object(forKey:)
宛乃,這是標(biāo)準(zhǔn)用法
參考鏈接:在UserDefaults中object(forKey:)和value(forKey:)的區(qū)別
參考
NSUserDefaults — A Swift Introduction
[NSUserDefaults synchronize] is Planned to be Deprecated
以上大多是一些需要注意的問題。關(guān)于Swift的常見用法运授,已經(jīng)有很多博客在詳述了烤惊,可以參考:Swift:UserDefaults協(xié)議(Swift視角下的泛字符串類型API)