首页 > 代码库 > iOS学习笔记-数据持久化

iOS学习笔记-数据持久化

       在iOS学习过程中,有时候需要保持用户数据,比如登录信息、用户的设置选项等,这时候就需要学习数据持久化操作,本节主要学习iOS数据持久化相关的知识。

       数据持久化的方式有四种:

              1).写入plist文件(属性列表)

              2).偏好设置 

              3).归档(NSKeyedArchiver)

              4).NSData

        下面分别举例说明四种方式的适用场合以及用法。

        1. 写入plist文件(属性列表)

        1.1 plist可以存储哪些数据

              属性列表是一种XML格式的文件,拓展名为plist
              如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中。
              由于plist文件的root只有Array和Dictionary两种类型,所以最好只保持它们对应数据类型的数据。

              技术分享

             比如当你存储字符串类型的数据的时候,Type就为空了。

            技术分享

           1.2 如何存储

           

	#pragma mark - 存储数据
	
	- (IBAction)btnSaveData_Click:(UIButton *)sender
	{
	    NSString *str = @"hello";
	    NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"test_str.plist"];
	    BOOL result = [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
	    if (result)
	    {
	        NSLog(@"存储数据成功");
	    }
	    else
	    {
	        NSLog(@"存储数据失败!");
	    }
	}

             程序运行结果如下:

             技术分享

自动生成的文件:

 技术分享


          

     注意:plist不能存储自定义对象类型!

    

	<span style="color:#000000;">#pragma mark - 存储数据
	
	- (IBAction)btnSaveData_Click:(UIButton *)sender
	{
	    // 文件的沙河路径
	    NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"test_obj.plist"];
	    // 创建Student对象
	    CYStudent *stu = [[CYStudent alloc]init];
	    stu.name = @"zhangsan";
	    stu.age = 18;
	    NSArray *arrTmp = @[stu];
	    BOOL result = [arrTmp writeToFile:filePath atomically:YES];
	    if (result)
	    {
	        NSLog(@"存储数据成功");
	    }
	    else
	    {
	        NSLog(@"存储数据失败!");
	    }
	}</span>
     程序运行结果如下:

   技术分享

      1.3 plist文件的存储与读取过程
      技术分享  


    2. 偏好设置

    2.1 使用场景

        很多iOS应用都支持偏好设置,比如保存用户名、密码、字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能
    每个应用都有个NSUserDefaults实例,通过它来存取偏好设置。比如,保存用户名、字体大小、是否自动登录。
    2.2 如何使用
    登录成功后保存数据:

   

    [[NSUserDefaults standardUserDefaults]setObject:self.txtAccount.text forKey:@"account"];
    [[NSUserDefaults standardUserDefaults]setObject:self.txtPWD.text forKey:@"pwd"];
    [[NSUserDefaults standardUserDefaults]setBool:self.swchRememberPWD.on forKey:@"isRememberPWD"];
    [[NSUserDefaults standardUserDefaults]setBool:self.swchAutoLogin.on forKey:@"isAutoLogin"];



        再次登录时读取数据:
        // 获取偏好设置中的数据
    self.txtAccount.text = [[NSUserDefaults standardUserDefaults]objectForKey:@"account"];
    self.swchAutoLogin.on = [[NSUserDefaults standardUserDefaults]boolForKey:@"isAutoLogin"];
    self.swchRememberPWD.on = [[NSUserDefaults standardUserDefaults]boolForKey:@"isRememberPWD"];
    self.txtPWD.text = [[NSUserDefaults standardUserDefaults]objectForKey:@"pwd"];

        注意:UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法强制写入
    [defaults synchornize];

       3. 归档(NSKeyedArchiver)

       3.1 使用场合

             如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,可以直接用NSKeyedArchiver进行归档和恢复
     不是所有的对象都可以直接用这种方法进行归档,只有遵守了NSCoding协议的对象才可以
     NSCoding协议有2个方法:
       1)encodeWithCoder:
        每次归档对象时,都会调用这个方法。一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:方法归档实例变量
       2)initWithCoder:
        每次从文件中恢复(解码)对象时,都会调用这个方法。一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey方法解码实例变量

       3.2 如何使用

       1> 自定义实体类
    
   @interface CYContact : NSObject

   /** 姓名 */
   @property (nonatomic,copy) NSString *name;

   /** 电话 */
   @property (nonatomic,copy) NSString *phone;

   @end



      2> 实现NSCoding协议方法
   
   #pragma mark - NSCoding协议方法
   /*
   Encodes the receiverusing a given archiver
   通过一个给定的archiver把消息接收者进行编码。
   当接收到encodeObject消息的时候,类终端encodeWithCoder方法被调用。
   */
   - (void)encodeWithCoder:(NSCoder *)aCoder
   {
       [aCoder encodeObject:_name forKey:CYNameKey];
       [aCoder encodeObject:_phone forKey:CYPhoneKey];
   }

   /*
   Returns an objectinitialized from data in a given unarchiver. (required)
   从一个给定unarchiver的数据中返回一个初始化对象。
   */
   - (id)initWithCoder:(NSCoder *)aDecoder
   {
       if (self = [super init])
       {
           _name = [aDecoder decodeObjectForKey:CYNameKey];
           _phone = [aDecoder decodeObjectForKey:CYPhoneKey];
       }
   return self;
   }

   /*
   Returnsa new instance that’s a copy of the receiver
   返回消息接收者的一个复制的新实例。
   */
   - (id)copyWithZone:(NSZone *)zone
   {
       CYContact *copy = [[[self class] allocWithZone:zone] init];
       copy.name = [self.name copyWithZone:zone];
       copy.phone = self.phone;
       return copy;
   }

      3> 归档
   [NSKeyedArchiver archiveRootObject:self.contacts toFile:CYFilePath];

      4> 接档
   self.contacts = [NSKeyedUnarchiver unarchiveObjectWithFile:CYFilePath];



      5> 注意点

      如果父类也遵守了NSCoding协议,请注意:
     ? 应该在encodeWithCoder:方法中加上一句[super encodeWithCode:encode];确保继承的实例变量也能被编码,即也能被归档
     ? 应该在initWithCoder:方法中加上一句self = [super initWithCoder:decoder];确保继承的实例变量也能被解码,即也能被恢复

     4. NSData

     4.1 使用场合

     使用archiveRootObject:toFile:方法可以将一个对象直接写入到一个文件中,但有时候可能想将多个对象写入到同一个文件中,那么   就要使用NSData来进行归档对象
     NSData可以为一些数据提供临时存储空间,以便随后写入文件,或者存放从磁盘读取的文件内容。可以使用[NSMutableData data]创建  可变数据空间

     技术分享

     

      注:黑色箭头表示将对象归档到文件中,红色箭头表示从文件中恢复对象

      4.2 如何使用

     

	#pragma mark - 存储数据
	
	- (IBAction)btnSaveData_Click:(UIButton *)sender
	{
	    // NSData-归档2个Person对象到同一文件中
	    // 实例化对象
	    CYStudent *stu1 = [CYStudent studentWithName:@"zhangsan" age:18];
	    CYStudent *stu2 = [CYStudent studentWithName:@"lisi" age:20];
	    // 新建一块可变数据区
	    NSMutableData *data = [NSMutableData data];
	    // 将数据区连接到一个NSKeyedArchiver对象
	    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
	    // 开始存档对象,存档的数据都会存储到NSMutableData中
	    [archiver encodeObject:stu1 forKey:@"stu1"];
	    [archiver encodeObject:stu2 forKey:@"stu2"];
	    // 存档完毕(一定要调用这个方法)
	    [archiver finishEncoding];
	    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"datas.data"];
	    // 将存档的数据写入文件
	    BOOL result = [data writeToFile:path atomically:YES];
	
	    if (result)
	    {
	        NSLog(@"存储数据成功");
	    }
	    else
	    {
	        NSLog(@"存储数据失败!");
	    }
	}
	
	#pragma mark - 读取数据
	
	- (IBAction)btnReadData_Click:(UIButton *)sender
	{
	    // NSData-从同一文件中恢复2个Person对象
	    // 从文件中读取数据
	    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"datas.data"];
	    NSData *data = [NSData dataWithContentsOfFile:path];
	    // 根据数据,解析成一个NSKeyedUnarchiver对象
	    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
	    CYStudent *stu1 = [unarchiver decodeObjectForKey:@"stu1"];
	    CYStudent *stu2 = [unarchiver decodeObjectForKey:@"stu2"];
	    // 恢复完毕
	    [unarchiver finishDecoding];
	    NSLog(@"%@",stu1);
	    NSLog(@"%@",stu2);
	}
	
	#pragma mark - 利用归档实现深复制
	
	- (IBAction)btnDeepCopy_Click:(UIButton *)sender
	{
	    // 比如对一个CYStudent对象进行深复制
	    // 临时存储stu1的数据
	    CYStudent *stu1 = [CYStudent studentWithName:@"zhangsan" age:18];
	    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:stu1];
	    // 解析data,生成一个新的Person对象
	    CYStudent *stu2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
	    // 分别打印内存地址
	    NSLog(@"stu1:%p", stu1); // stu1:0x7bdb32b0
	    NSLog(@"stu2:%p", stu2); // stu2:0x7bdb6b80
	}









































        

iOS学习笔记-数据持久化