Swift下UserDefaults的常見使用和注意事項(xiàng)

前言

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的方式

  1. 創(chuàng)建UserDefaults的拓展
  2. 創(chuàng)建枚舉值存儲(chǔ)需要的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) 

注意事項(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

參考鏈接:UserDefaults預(yù)設(shè)值

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)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吁朦,隨后出現(xiàn)的幾起案子柒室,更是在濱河造成了極大的恐慌,老刑警劉巖逗宜,帶你破解...
    沈念sama閱讀 211,423評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雄右,死亡現(xiàn)場(chǎng)離奇詭異空骚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)擂仍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門囤屹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人逢渔,你說我怎么就攤上這事肋坚。” “怎么了肃廓?”我有些...
    開封第一講書人閱讀 157,019評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵智厌,是天一觀的道長。 經(jīng)常有香客問我盲赊,道長铣鹏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,443評(píng)論 1 283
  • 正文 為了忘掉前任哀蘑,我火速辦了婚禮诚卸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绘迁。我一直安慰自己合溺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評(píng)論 6 385
  • 文/花漫 我一把揭開白布脊髓。 她就那樣靜靜地躺著辫愉,像睡著了一般栅受。 火紅的嫁衣襯著肌膚如雪将硝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,798評(píng)論 1 290
  • 那天屏镊,我揣著相機(jī)與錄音依疼,去河邊找鬼。 笑死而芥,一個(gè)胖子當(dāng)著我的面吹牛律罢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棍丐,決...
    沈念sama閱讀 38,941評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼误辑,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了歌逢?” 一聲冷哼從身側(cè)響起巾钉,我...
    開封第一講書人閱讀 37,704評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秘案,沒想到半個(gè)月后砰苍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體潦匈,經(jīng)...
    沈念sama閱讀 44,152評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評(píng)論 2 327
  • 正文 我和宋清朗相戀三年赚导,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茬缩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,629評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吼旧,死狀恐怖凰锡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情圈暗,我是刑警寧澤寡夹,帶...
    沈念sama閱讀 34,295評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站厂置,受9級(jí)特大地震影響菩掏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昵济,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評(píng)論 3 313
  • 文/蒙蒙 一智绸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧访忿,春花似錦瞧栗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卧斟,卻和暖如春殴边,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背珍语。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評(píng)論 1 266
  • 我被黑心中介騙來泰國打工锤岸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人板乙。 一個(gè)月前我還...
    沈念sama閱讀 46,333評(píng)論 2 360
  • 正文 我出身青樓是偷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親募逞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蛋铆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 源碼加翻譯 #import <Foundation/NSArray.h> #import <Foundation/...
    CAICAI0閱讀 1,148評(píng)論 0 50
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中放接,可以允許開發(fā)者通過K...
    暮年古稀ZC閱讀 2,132評(píng)論 2 9
  • 關(guān)于鍵值編碼 鍵值編碼(KVC)是一種由NSKeyValueCoding非正式協(xié)議提供的機(jī)制刺啦,對(duì)象采用該機(jī)制來提供...
    漸z閱讀 913評(píng)論 0 0
  • 官方文檔點(diǎn)這里:Key-Value Coding Programming Guide、NSKey?Value?Co...
    阿斯蘭iOS閱讀 1,409評(píng)論 0 1
  • 這聽起來是一個(gè)很明顯的問題磕秤,但是它真這么簡單嗎? 我聽到過公司里的一些開發(fā)人員討論這個(gè)問題捧韵。當(dāng)時(shí)我的一位高級(jí)工程師...
    堯淳閱讀 243評(píng)論 0 2