最近開發(fā)過程中遇到一個線上Crash,在api發(fā)布到線上以后富稻,app崩潰率提升到30%铲球,打開bugly查看崩潰原因名扛,提示attempt to insert no-property list in to ..... too long
,原來是請求服務(wù)器配置的數(shù)據(jù)括儒,在存入UserDefaults之后導(dǎo)致的crash绕沈。
1. 一段可疑的代碼
LietomeProvider.request(.forumConfig, completion: { result in
switch result {
case .success(let response):
guard response.code == NoneError else {
let last = UserDefaults.standard.dictionary(forKey: "example.cn.main.forum")
if last != nil {
self.forumConfig = ForumConfig.mj_object(withKeyValues: last)
}
return
}
let dataJSON = response.dataJSON
let remote = dataJSON.dictionaryObject
self.forumConfig = ForumConfig.mj_object(withKeyValues: remote)
UserDefaults.standard.setValue(remote, forKey: "example.cn.main.forum") // 崩潰在這里
UserDefaults.standard.synchronize()
case .failure:
break
}
})
上面的代碼為什么會崩潰呢?原來讀取的服務(wù)器配置信息remote是一個字典帮寻,這個字典包含很多的key-value信息乍狐,但是由于api開發(fā)人員忘了將一個key-value賦值,導(dǎo)致只有key而沒有value固逗,這樣就導(dǎo)致存入UserDefaults的內(nèi)容為null或者nil浅蚪,我們知道NSNull是沒有實現(xiàn)NSCoding協(xié)議的藕帜,這就導(dǎo)致存入UserDefaults的時候崩潰了。
2. 使用SwiftyUserDefaults替代UserDefaults
系統(tǒng)提供給我們的組件相當于是一個毛坯房惜傲,使用相當簡單洽故,但是會有很多的坑,所以可以對系統(tǒng)組件進行封裝盗誊,實現(xiàn)自定義的目的时甚;也可以借助第三方組件,來替代系統(tǒng)的UserDefaults哈踱,SwiftyUserDefaults就是這樣一個組件荒适,它的使用方法相當簡單,現(xiàn)定義需要存儲的key开镣,然后通過swift的下標[]
語法進行存取操作刀诬,如下代碼所示,筆者添加了注釋哑子,讀者朋友應(yīng)該很容易理解舅列。
// FRUserDefaultsKeys.swift
import SwiftyUserDefaults
extension DefaultsKeys {
/// 論壇的配置信息
static let FRConfigureCachedKey = DefaultsKey<[String:Any]?>("example.cn.forum.configure")
/// 論壇版塊列表的瀏覽歷史記錄
static let FRSectionUserDefaultCacheKey = DefaultsKey<[String:Any]?>("example.cn.main.forum.history")
/// 論壇搜索的最新一條搜索歷史記錄
static let frLatestSearchedKey = DefaultsKey<String>("fr.example.searchedKey")
/// 論壇搜索的所有歷史記錄
static let frSearchedKeywordHistory = DefaultsKey<[String]>("fr.example.searchedHistory")
}
// FRConfigure.swift
import SwiftyUserDefaults
LietomeProvider.request(.forumConfig, completion: { result in
switch result {
case .success(let response):
guard response.code == NoneError else {
let last = Defaults[.FRConfigureCachedKey] // 使用SwiftyDefaults
if last != nil {
self.forumConfig = ForumConfig.mj_object(withKeyValues: last)
}
return
}
let dataJSON = response.dataJSON
let remote = dataJSON.dictionaryObject
self.forumConfig = ForumConfig.mj_object(withKeyValues: remote)
Defaults[.FRConfigureCachedKey] = remote // 使用SwiftyDefaults
case .failure:
break
}
})
使用該組件,可以避免UserDefaults導(dǎo)致的crash卧蜓,而且不必再通過手寫字符串的方式作為key來存取數(shù)據(jù)帐要,這無疑減小了出錯的概率。
SwiftyUserDefaults的項目非常小巧弥奸,它就是結(jié)合swify的靈活的語法實現(xiàn)了對UserDefaults的封裝榨惠,有興趣的讀者可以到github看一下它的代碼。在后續(xù)的文章中盛霎,筆者會對該組件的代碼做一個解析和分享赠橙。