目錄
一、NSUserDefaults的基本使用
二、使用NSUserDefaults需注意
一、NSUserDefaults的基本使用
NSUserDefaults專門用來(lái)把偏好設(shè)置數(shù)據(jù)存儲(chǔ)在Library/Preferences文件夾下,其實(shí)NSUserDefaults本質(zhì)上是一個(gè)字典,偏好設(shè)置數(shù)據(jù)就存儲(chǔ)在這個(gè)字典里,字典又會(huì)被存儲(chǔ)在Library/Preferences文件夾下的一個(gè)plist文件里,也就是說(shuō)NSUserDefaults這個(gè)類其實(shí)只是給我們提供了更加方便的方式去存儲(chǔ)和讀取數(shù)據(jù)而已。
- 存儲(chǔ)和讀取基本數(shù)據(jù)類型的數(shù)據(jù)
- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
- (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName;
- (void)setFloat:(float)value forKey:(NSString *)defaultName;
- (void)setDouble:(double)value forKey:(NSString *)defaultName;
- (BOOL)boolForKey:(NSString *)defaultName;
- (NSInteger)integerForKey:(NSString *)defaultName;
- (float)floatForKey:(NSString *)defaultName;
- (double)doubleForKey:(NSString *)defaultName;
- 存儲(chǔ)和讀取系統(tǒng)自帶對(duì)象類型的數(shù)據(jù)(NSString、NSArray、NSDictionary、NSData等)
- (void)setObject:(nullable id)value forKey:(NSString *)defaultName;
- (id)objectForKey:(NSString *)defaultName;
- 存儲(chǔ)和讀取自定義對(duì)象類型的數(shù)據(jù)
使用NSUserDefaults存儲(chǔ)自定義對(duì)象類型的數(shù)據(jù)時(shí),必須先把對(duì)象歸檔為NSData,然后再存進(jìn)去,讀取的時(shí)候也必須先讀取出NSData,然后再反歸檔為對(duì)象。
舉個(gè)例子,假設(shè)有個(gè)Person類。
-----------Person.h-----------
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic, assign) NSInteger age;
@end
-----------Person.m-----------
#import "Person.h"
@implementation Person
@end
我們創(chuàng)建了一個(gè)person對(duì)象,并且想把它持久化在NSUserDefaults中。
-----------ViewController.m-----------
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
person.name = @"張三";
person.sex = @"男";
person.age = 11;
[[NSUserDefaults standardUserDefaults] setObject:person forKey:@"person"];
}
運(yùn)行,崩了,控制臺(tái)輸出'Attempt to insert non-property list object <Person: 0x600001536f20> for key person'
,說(shuō)是我們?cè)噲D給person這個(gè)key賦值一個(gè)person對(duì)象,但是這個(gè)person對(duì)象不是一個(gè)plist object。現(xiàn)在我們?yōu)閜erson對(duì)象添加一下歸檔、反歸檔。
-----------ViewController.m-----------
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
person.name = @"張三";
person.sex = @"男";
person.age = 11;
// 歸檔
NSData *writeData = [NSKeyedArchiver archivedDataWithRootObject:person];
// 存儲(chǔ)
[[NSUserDefaults standardUserDefaults] setObject:writeData forKey:@"person"];
// 讀取
NSData *readData = [[NSUserDefaults standardUserDefaults] objectForKey:@"person"];
// 反歸檔
Person *man = [NSKeyedUnarchiver unarchiveObjectWithData:readData];
NSLog(@"%@ %@ %ld", man.name, man.sex, man.age);
}
運(yùn)行,又崩了,控制臺(tái)輸出'-[Person encodeWithCoder:]: unrecognized selector sent to instance 0x600001e810e0'
,說(shuō)是person對(duì)象沒(méi)有實(shí)現(xiàn)encodeWithCoder:方法。這是因?yàn)樵跉w檔的過(guò)程中,系統(tǒng)會(huì)自動(dòng)調(diào)用對(duì)象的encodeWithCoder:方法,這個(gè)方法存在于NSCoding協(xié)議里,我們要在這個(gè)方法里告訴系統(tǒng)對(duì)象的哪些屬性需要?dú)w檔并一一encode掉。現(xiàn)在我們?yōu)閜erson對(duì)象實(shí)現(xiàn)一下encodeWithCoder:方法。
-----------Person.m-----------
#import "Person.h"
@interface Person () <NSCoding>
@end
@implementation Person
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeObject:_sex forKey:@"sex"];
[aCoder encodeObject:@(_age) forKey:@"age"];
}
@end
運(yùn)行,又崩了,控制臺(tái)輸出'-[Person initWithCoder:]: unrecognized selector sent to instance 0x600001e810e0'
,說(shuō)是person對(duì)象沒(méi)有實(shí)現(xiàn)initWithCoder:方法。這是因?yàn)樵诜礆w檔的過(guò)程中,系統(tǒng)會(huì)自動(dòng)調(diào)用對(duì)象的initWithCoder:方法,這個(gè)方法也存在于NSCoding協(xié)議里,我們要在這個(gè)方法里告訴系統(tǒng)對(duì)象的哪些屬性需要反歸檔并一一decode掉。現(xiàn)在我們?yōu)閜erson對(duì)象實(shí)現(xiàn)一下initWithCoder:方法。
-----------Person.m-----------
#import "Person.h"
@interface Person () <NSCoding>
@end
@implementation Person
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeObject:_sex forKey:@"sex"];
[aCoder encodeObject:@(_age) forKey:@"age"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
_name = [aDecoder decodeObjectForKey:@"name"];
_sex = [aDecoder decodeObjectForKey:@"sex"];
_age = [[aDecoder decodeObjectForKey:@"age"] integerValue];
}
return self;
}
@end
運(yùn)行,ok了。
通過(guò)上面的例子,我們可能會(huì)感覺(jué)到使用NSUserDefaults存儲(chǔ)自定義對(duì)象類型的數(shù)據(jù)時(shí)的歸檔和反歸檔操作有些麻煩,麻煩一在于我們要寫對(duì)象歸檔為NSData和NSData反歸檔為對(duì)象的代碼,麻煩二在于我們要為對(duì)象實(shí)現(xiàn)NSCoding協(xié)議里的encodeWithCoder:和initWithCoder:方法。
因此為了簡(jiǎn)化開(kāi)發(fā),針對(duì)麻煩一,我們可以考慮給NSUserDefaults添加一個(gè)分類為它擴(kuò)展一對(duì)兒存取對(duì)象的方法,在方法內(nèi)部完成對(duì)象歸檔為NSData和NSData反歸檔為對(duì)象的代碼;針對(duì)麻煩二,我們不可能為項(xiàng)目里每個(gè)需要持久化的對(duì)象都讓它的類遵守NSCoding協(xié)議并實(shí)現(xiàn)協(xié)議里的encodeWithCoder:和initWithCoder:方法,所以我們可以考慮為根類NSObject添加一個(gè)分類,在分類里為NSObject擴(kuò)展遵守NSCoding協(xié)議并實(shí)現(xiàn)協(xié)議里的encodeWithCoder:和initWithCoder:方法,而且在協(xié)議方法的實(shí)現(xiàn)里我們還要用Runtime獲取屬性列表來(lái)做encode和decode,這將極大地提升我們的開(kāi)發(fā)效率。
二、使用NSUserDefaults需注意
需要注意的是,當(dāng)我們往NSUserDefaults里存儲(chǔ)一個(gè)數(shù)據(jù)的時(shí)候,它不是立馬就存儲(chǔ)進(jìn)Library/Preferences文件夾下的plist文件里,而是會(huì)先存儲(chǔ)在NSUserDefaults的cache里,然后每隔一定的時(shí)間再統(tǒng)一將cache里的數(shù)據(jù)統(tǒng)一存儲(chǔ)進(jìn)plist文件,所以我們存取數(shù)據(jù)就有可能出錯(cuò)。
為了確保NSUserDefaults立馬把數(shù)據(jù)存儲(chǔ)進(jìn)plist文件里,我們?cè)趕et完數(shù)據(jù)后最好手動(dòng)調(diào)用一下[[NSUserDefaults standardUserDefaults] synchronize]
方法。