iOS 沙盒NSUserDefaults 存储可变数组问题 数据持久化
前言
NSUserDefaults支持的数据类型有:NSNumber(NSInteger、float、double),NSString,NSDate,NSArray,NSDictionary,BOOL.
附:NSUserDefaults 虽然本身不支持自定义对象的存储,不过它支持NSData的类型。所以当我们要在NSUserDefaults中存储的是自定义的对象的时候,需要将该自定义对象转成NSData存储。而自定义对象转data的方式我们通过<NSCoding>来实现。这里提前讲到,下面会详细介绍。
1、NSUserDefaults 存储数组问题
①如果数组中的对象不是自定义的对象,那么可直接存储。如:
NSArray *array = @[@"1", @"2", @"3", @"4"];
[[NSUserDefaults standardUserDefaults] setObject:array forKey:@"key"];
[[NSUserDefaults standardUserDefaults] synchronize];
②如果数组中的对象是自定义的对象,那么需要先让这个自定义类实现<NSCoding>协议中的- (id) initWithCoder: (NSCoder *)coder方法和- (void) encodeWithCoder: (NSCoder *)coder方法,然后把该自定义的类对象编码到NSData中,再从NSUserDefaults中进行读取。
//User.h @interface User : NSObject<NSCoding> //注意:这里需要实现NSCoding协议 @property (nonatomic, copy) NSString *realName; @property (nonatomic, copy) NSString *nickName; @property (nonatomic, copy) NSString *password; @end //User.m @implementation User - (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { self.realName = [aDecoder decodeObjectForKey:@"realName"]; self.nickName = [aDecoder decodeObjectForKey:@"nickName"]; self.password = [aDecoder decodeObjectForKey:@"password"]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.realName forKey:@"realName"]; [aCoder encodeObject:self.nickName forKey:@"nickName"]; [aCoder encodeObject:self.password forKey:@"password"]; } @end 这时,存储自定义对象的数组的获取与保存方法如下 //存储 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSArray *array = @[customObject1, customObject2, customObject3, customObject4]; NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:array]; [userDefaults setObject:arrayData forKey:@"arrayKey"]; [userDefaults synchronize]; //取出 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSData *arrayData = [userDefaults objectForKey:@"arrayKey"]; NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:arrayData];
如果你想要从 NSUserDefaults
中移除一个特定的键(key)及其对应的值,你可以使用 removeObjectForKey:
方法。这个方法会从 NSUserDefaults
的标准用户默认设置中删除与给定键相关联的值。
下面是如何使用 removeObjectForKey:
方法的示例代码:
// 获取NSUserDefaults的实例 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // 假设你想要移除的键是"myKey" NSString *keyToRemove = @"myKey"; // 从NSUserDefaults中移除该键及其对应的值 [defaults removeObjectForKey:keyToRemove]; // 同步更改到磁盘上,确保更改被保存 [defaults synchronize];
请注意,在调用 removeObjectForKey:
方法后,你还需要调用 synchronize
方法来确保你的更改被保存到磁盘上。不过,从 iOS 12 开始,Apple 推荐在大多数情况下不需要显式调用 synchronize
,因为系统会在适当的时候自动同步 NSUserDefaults
。然而,在某些特殊情况下,特别是当你需要立即确保数据被写入磁盘时,显式调用 synchronize
仍然是一个好习惯。
还要注意的是,如果尝试移除一个不存在的键,removeObjectForKey:
方法将不会有任何效果,也不会引发错误。因此,在调用此方法之前,你不需要检查键是否存在。
2、NSUserDefaults 存储自定义对象的问题见上
3、修改存储在NSUserDefaults中的数组
废话不多说上代码,项目中遇到的问题是:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSMutableArray *array = [userDefaults objectForKey:@"theArrayKey"]]; [array addObject:@"some new value"]; [userDefaults setObject: array forKey:@"theArrayKey"]; //会卡住主程序 [[NSUserDefaults standardUserDefaults] synchronize];
这句话取出了数组,可是当对数组添加元素后,进行存储时卡在了下面这句话:[userDefaults setObject: array forKey:@"theArrayKey"];
时候会把主线程卡住,但是不崩溃,不知道为何,就Google了,stackoverflow给了解答办法,
When you store mutable objects to NSUserDefaults, it stores an immutable copy of it so you can't change it directly like that. You have to get the mutable copy out of defaults, change it, and then set it back, replacing old object in defaults.
即:修改存储在NSUserDefaults中的数组:我们从NSUserDefaults中取出的数组是不可变的(因为NSUserDefaults 存储的对象全是不可变的)。所以当我们需要修改存储在NSUserDefaults中的数组时,需要用一个新的可变数组来保存之前的值,再修改,之后再保存,即修改的过程应该如下:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSMutableArray *array = [userDefaults objectForKey:@"theArrayKey"]]; NSMutableArray *mutableCopyArray = [array mutableCopy]; //重要步骤操作mutableCopyArray [mutableCopyArray addObject:@"some new value"]; [userDefaults setObject: mutableCopyArray forKey:@"theArrayKey"]; [[NSUserDefaults standardUserDefaults] synchronize];
总结:NSUserDefaults 存储的对象全是不可变的(这一点非常关键,弄错的话程序会出bug),即存进NSUserDefaults的对象会变成不可变的,同样取出来的对象肯定是不可变的。
其它
//存储 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSString *strID = pUser.Tradeaccount;//交易账户 NSString *strContent = @"notipsnexttime_on";//方向 NSString *str = [NSString stringWithFormat:@"%@%@",strID, strContent]; NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:str]; [userDefaults setObject:arrayData forKey:@"notips"]; [userDefaults synchronize]; //取出 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSData *arrayData = [userDefaults objectForKey:@"notips"]; NSString *str = [NSKeyedUnarchiver unarchiveObjectWithData:arrayData]; [userDefaults synchronize];