前些時間,@Vong 同窗在咱們知識小集羣裏發了一段用 NSUserDefaults
存儲一個 NSDictionary
字典對象的測試代碼,雖然代碼看起來彷佛很正常,可是運行的時候報錯了,根據羣裏你們的討論結果,本文整理總結一下。html
咱們先來看一下這段測試代碼:express
- (void)test {
NSDictionary *dict = @{@1: @"甲",
@2: @"乙",
@3: @"丙",
@4: @"丁"};
[[NSUserDefaults standardUserDefaults] setObject:dict forKey:@"testKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
複製代碼
上述代碼先構建了一個 dict
字典對象,而後把它往 NSUserDefaults
裏存儲,看起來彷佛沒什麼毛病,但在 Xcode 中運行的時候,卻報以下錯誤:bash
[User Defaults] Attempt to set a non-property-list object {
3 = "\U4e19";
2 = "\U4e59";
1 = "\U7532";
4 = "\U4e01";
} as an NSUserDefaults/CFPreferences value for key `testKey`
複製代碼
難道是由於上述 NSDictionary
對象的 Key
爲 NSNumber
的問題?NSDictionary
的 Key
必定要爲 NSString
嗎?顯然不是的,咱們在上述代碼的 dict
初始化後打個斷點,而後在命令行裏 po dict
,是能夠正確看到控制檯輸出 dict
對象的內容。再來看一下 NSDictionary
的定義:微信
@interface NSDictionary<KeyType, ObjectType> (NSDictionaryCreation)
- (instancetype)initWithObjects:(NSArray<ObjectType> *)objects forKeys:(NSArray<id<NSCopying>> *)keys;
...
@end
複製代碼
咱們能夠知道,NSDictionary
的 Key
只要是遵循 NSCopying
協議的就行,顯然 NSNumber
是遵循的(可自行查看 NSNumber
的定義),所以用 NSNumber
做爲 NSDictionary
的 Key
是沒問題的。app
可是,當咱們把上述 dict
的 Key
改成字符串的形式(注意跟上面的區別),以下:測試
NSDictionary *dict = @{@"甲": @1,
@"乙": @2,
@"丙": @3,
@"丁": @4};
複製代碼
此時,dict
是能夠正常存儲到 NSUserDefaults
中的。ui
因此,問題確實是出在 dict
的 Key
上,但究竟是什麼緣由呢?spa
咱們須要從 NSUserDefaults
的 setObject:forKey:
方法入手分析:命令行
- (void)setObject:(id)value forKey:(NSString *)defaultName;
複製代碼
經過查看蘋果文檔:https://developer.apple.com/documentation/foundation/nsuserdefaults/1414067-setobject?language=objc ,有這麼一段描述:rest
The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects.
也就是說,能往 NSUserDefaults
裏存儲的對象只能是 property list objects
,包括 NSData
, NSString
, NSNumber
, NSDate
, NSArray
, NSDictionary
,且對於 NSArray
和 NSDictionary
這兩個容器對象,它們所包含的內容也必需是 property list objects
。
那麼,什麼是 Property List
呢?咱們能夠查看蘋果的這份文檔:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html
Property lists are based on an abstraction for expressing simple hierarchies of data, ... By convention, each Cocoa and Core Foundation object listed in Table 2-1 is called a property-list object, ...
... If an array or dictionary contains objects that are not property-list objects, then you cannot save and restore the hierarchy of data using the various property-list methods and functions.
And although NSDictionary and CFDictionary objects allow their keys to be objects of any type, if the keys are not string objects, the collections are not property-list objects.
重點看最後一句話,雖然 NSDictionary
和 CFDictionary
對象的 Key
能夠爲任何類型(只要遵循 NSCopying
協議便可),可是若是當 Key
不爲字符串 string
對象時,此時這個字典對象就不能算是 property list objects
了,因此它就往 NSUserDefaults
中存儲,否則就會報錯:
Attempt to set a non-property-list objec ... as an NSUserDefaults/CFPreferences value for key ...
複製代碼
以上,真相大白。
By the way,不單單是 NSUserDefaults
的 setObject:forKey:
方法,但凡使用系統其餘任何方法涉有及到 Property List
對象,都要注意上面的問題。
知識小集是一個團隊公衆號,每週都會有原創文章分享,咱們的文章都會在公衆號首發。歡迎關注查看更多內容。
另外,咱們開了微信羣,方便你們溝通交流技術。目前知識小集1號羣已滿,新開了知識小集2號羣,有興趣的能夠掃碼加入,沒有iOS限制,移動開發的同窗都歡迎。因爲微信羣掃碼加羣有100的限制,因此若是掃碼沒法加入的話,能夠先加微信號 coldlight_hh 或者 wsy9871,再拉進羣哈。