iOS开发中数据持久化(二):NSUserDefaults实现存储数据

这篇文章主要讲述NSUserDefaults的简单使用
Demo地址
对应实现的文件是NSUserDefaultViewController.m文件和person.m文件
存储数据成功后,在沙盒中查看相应的存储文件数据如下图所示:

NSUserDefault存储数据

注意,NSUserDefaults除了存储一些基本的数据类型之外,还可以存储NSArray、NSDictionary等一些基本的数据类型,如果想要存储自定的数据类型,执行将自定义对象转化为NSData,然后使用NSUserDefaults存储,上图中Person就是自定义对象,注意,每次读取获得的数据类型都是不可变类型,例如,你存储进去的是NSMutableArray类型,但是读取出来之后就是NSArray类型

如何查看app的沙盒文件

模拟机

在这里,先说一下,如何查看文件存储的沙盒位置
如果是使用xcode提供的模拟机,那么文件就在自己电脑上,使用

//获取沙盒根目录
NSString *directory = NSHomeDirectory();
NSLog(@"directory:%@", directory);

这样可以获得app沙盒文件的根目录

真机

如果使用的是真机测试
在xcode中,点击windows->Devices And Simulators,找到我们对应的app名称


image.png

选中,点击下方的齿轮


image.png

点击Download Container即可将app的沙盒文件下载到电脑上,然后可以查看

NSUserDefaults实现

首先定义两个按钮,一个用于写入数据,一个用于读取数据
在NSUserDefaultViewController.h文件中

@property (nonatomic, strong) UIButton *NSUserDefaultWriteButton;
@property (nonatomic, strong) UIButton *NSUserDefaultReadButton;

之后,在.m中实现相应的懒加载和布局

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.title = @"NSUserDefault存储";
    self.navigationController.navigationBar.translucent = NO;
    self.view.backgroundColor = [UIColor whiteColor];
    
    [self.view addSubview:self.NSUserDefaultWriteButton];
    [self.NSUserDefaultWriteButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view);
        make.centerY.equalTo(self.view);
        make.width.greaterThanOrEqualTo(@0);
        make.height.equalTo(@30);
    }];
    [self.view addSubview:self.NSUserDefaultReadButton];
    [self.NSUserDefaultReadButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view);
        make.top.equalTo(self.NSUserDefaultWriteButton.mas_bottom).offset(20);
        make.width.greaterThanOrEqualTo(@0);
        make.height.equalTo(@30);
    }];
}

- (UIButton *)NSUserDefaultWriteButton {
    if (_NSUserDefaultWriteButton) {
        return _NSUserDefaultWriteButton;
    }
    _NSUserDefaultWriteButton = [[UIButton alloc] initWithFrame:CGRectZero];
    _NSUserDefaultWriteButton.titleLabel.font = [UIFont systemFontOfSize:18];
    [_NSUserDefaultWriteButton setTitle:@"NSUserDefault写入数据" forState:UIControlStateNormal];
    [_NSUserDefaultWriteButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_NSUserDefaultWriteButton addTarget:self action:@selector(nsUserDefaultWirteData) forControlEvents:UIControlEventTouchUpInside];
    return _NSUserDefaultWriteButton;
}


- (UIButton *)NSUserDefaultReadButton {
    if (_NSUserDefaultReadButton) {
        return _NSUserDefaultReadButton;
    }
    _NSUserDefaultReadButton = [[UIButton alloc] initWithFrame:CGRectZero];
    _NSUserDefaultReadButton.titleLabel.font = [UIFont systemFontOfSize:18];
    [_NSUserDefaultReadButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_NSUserDefaultReadButton setTitle:@"NSUserDefault读出数据" forState:UIControlStateNormal];
    [_NSUserDefaultReadButton addTarget:self action:@selector(nsUserDefaultReadData) forControlEvents:UIControlEventTouchUpInside];
    return _NSUserDefaultReadButton;
}

- (void)nsUserDefaultWirteData {
   NSLog(@"NSUserDefault写入数据");
}

- (void)nsUserDefaultReadData {
   NSLog(@"NSUserDefault读出数据");
}

之后,我们就可以在按钮对应的点击方法中实现存储数据和读取数据

接下来一个一个说
先获取到唯一的NSUserDefaults对象

NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];

存储的代码写在nsUserDefaultWirteData方法中,读取的代码写在nsUserDefaultReadData中

1.整数

//存储整数
   [ud setInteger:111 forKey:@"INT"];
 //读取NSInteger
    NSInteger i = [[ud objectForKey:@"INT"] integerValue];
    NSLog(@"i = %ld",(long)i);

2.BOOL值

 //存储bool值
    [ud setBool:YES forKey:@"BOOL"];
//读取bool值
    BOOL isYES = [ud objectForKey:@"BOOL"];
    NSLog(@"isYES = %d",isYES);

3.浮点数

//存储浮点数
    [ud setFloat:1.8 forKey:@"float"];
//读取浮点数
    CGFloat f = [[ud objectForKey:@"float"] floatValue];
    NSLog(@"f = %f",f);

4.NSString字符串

//存储字符串NSString
    [ud setObject:@"落叶兮兮" forKey:@"Name"];
//读取NSString
    NSString *name = [ud objectForKey:@"Name"];
    NSLog(@"name = %@",name);

5.NSNumber

//存储NSNumber
    NSNumber *num = [NSNumber numberWithInteger:100];
    [ud setObject:num forKey:@"NUM"];
//读取NSNumber
    NSNumber *number = [ud objectForKey:@"NUM"];
    NSLog(@"number = %@",number);

6.数组NSArray

//存储数组
    NSArray *array = [NSArray arrayWithObjects:@"11",@"22",@"33",nil];
    [ud setObject:array forKey:@"ARRAY"];
//读取NSArray
    NSArray *arr = [ud objectForKey:@"ARRAY"];
    NSLog(@"arr = %@",arr);

7.字典NSDictionary

   NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    [dic setValue:@"落叶兮兮" forKey:@"Id1"];
    [dic setValue:@"雪花飞舞" forKey:@"Id2"];
    [ud setObject:dic forKey:@"Dictionary"];
//读取NSDictionary
    NSDictionary *dic = [ud objectForKey:@"Dictionary"];
    NSLog(@"dic id1 = %@",[dic objectForKey:@"Id1"]);
    NSLog(@"dic id2 = %@",[dic objectForKey:@"Id2"]);

注意,NSUserDefaults读取后的类型都是不可变的类型

还有其他的一些基本数据类型,比如NSDate,Double等,这里就不再写了,下面主要讲自定义对象的存储,这部分比较复杂

自定义对象Person

对于NSUserDefault存储自定义对象,相比其他类型,这部分是最复杂的,将这部分放在最后再写

我们都知道,存储自定义对象,应该将对象转换为NSData数据类型,然后将其存储到plist文件中,这里转换需要用到NSKeyedArchiver归档和NSKeyedUnarchiver解档

首先,我们新建一个文件,命名为Person
定义基本的属性和赋值方法
在person.h文件中,

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

- (void)setName:(NSString *)name age:(NSInteger)age;

在person.m文件中,

- (void)setName:(NSString *)name age:(NSInteger)age {
    self.name = name;
    self.age = age;
}

这样基本类Person就定义好了,在NSUserDefaultViewController中初始化并赋值
在nsUserDefaultWriteData方法中

Person *person = [[Person alloc] init];
[person setName:@"落叶兮兮" age:24];

之后,将Person转化为NSData对象

//使用error接收错误信息,一旦出错,error里面存储的就是错误信息
NSError *error = nil;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person requiringSecureCoding:YES error:&error];
if (error) {
        NSLog(@"写入数据时转换失败,失败的原因是%@",error);
    }

运行之后,报错


自定义对象转化为NSData对象报错

原因是Person类没有实现NSSecureCoding协议,关于这个协议方法,主要是为了数据安全,防止被别人篡改,具体的可以去苹果官方文档查看

接下来就很简单了,我们需要实现相应的协议方法
person类遵守<NSSecureCoding>

@interface Person : NSObject<NSSecureCoding>

点进去可以看到NSSecureCoding协议遵守<NSCoding>协议


NSSecureCoding协议

所以需要实现NSCoding协议的方法
在Person.m文件中

//1.编码方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"PersonName"];
    [aCoder encodeInteger:self.age forKey:@"PersonAge"];
}

//2.解码方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        self.name = [aDecoder decodeObjectForKey:@"PersonName"];
        self.age = [aDecoder decodeIntegerForKey:@"PersonAge"];
    }
    return self;
}

注意,根据NSSecureCoding协议的介绍,我们必须实现supportsSecureCoding方法,并且返回YES,这样才是支持NSSecureCoding协议

而且必须得是类方法,刚开始我写成了实例方法,结果一直报错,转化失败

+ (BOOL)supportsSecureCoding {
    NSLog(@"执行了这个方法");
    return YES;
}

此事执行转化方法,发现没有错误出现,接下来是读取,读取获得的是NSData对象,需要将其转化为Person自定义对象,使用解档

NSData *data = [ud objectForKey:@"Person"];
    Person *person = [[Person alloc] init];
    NSError *error = nil;
    person = [NSKeyedUnarchiver unarchivedObjectOfClass:[Person class] fromData:data error:&error];
    if (error) {
        NSLog(@"转化出错,出错的原因为%@",error);
    }
    NSLog(@"person name = %@",person.name);
    NSLog(@"person age = %ld",(long)person.age);

运行之后发现能够正常输出内容,转化类型无错误发生

9.存储路径

//NSUserDefault写入数据,默认放在library/Preferences文件夹下,以.plist文件存放
    
   NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *path = [paths objectAtIndex:0];
    NSLog(@"数据持久化的路径为:%@",path);

最好使用模拟机运行,模拟机运行之后获得path,直接在finder中command+G可以直接到存储的plist文件

如果使用真机,需要将数据包下载到电脑上才能看到,比较麻烦

总结

最终的数据存储图为:


NSUserDefault存储数据

Demo地址
对应实现的文件是NSUserDefaultViewController.m文件和person.m文件

总结

iOS开发中数据持久化总结(一)
iOS开发中数据持久化总结(二):NSUserDefault实现数据存储
ios开发中数据持久化总结(三):NSKeyArchive归档解档的实现
ios开发中数据持久化总结(四):使用FMDataBase存储数据

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容