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类遵守

@interface Person : NSObject

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


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存储数据

你可能感兴趣的:(iOS开发中数据持久化(二):NSUserDefaults实现存储数据)