• iOS 数据持久化(1):属性列表与对象归档


    1. 基础知识

    1.1 简介

          数据持久存储是一种非易失性存储,在重启动计算机或设备后也不会丢失数据。持久化技术主要用于MVC模型中的model层。其中目前再IOS平台上主要使用如下的四种技术:

    • 属性列表
    • 对象归档
    • SQLite3
    • Core Data

          其中需要注意的是,在IOS开发中除了上述4种最简单持久化技术外,还可以使用传统C语言I/O调用(比如fopen())读取和写入数据,也可以使用Cocoa的底层文件管理工具。

    1.2 沙盒(SandBox

           IOS中的沙盒机制(SandBox)是一种安全体系,它规定了应用程序只能在为该应用创建的文件夹内读取文件,不可以访问其他地方的内容。所有的非代码文件都保存在这个地方,比如图片、声音、属性列表和文本文件等。

    1.2.1 沙盒结构

        每个应用程序沙盒抖包含以下三个目录:

         1) Documents:

            应用程序可以将数据存储在Documents目录中。在此目录中的文件可以被共享。其中本文中的4种数据持久化技术都涉及该目录。

         2) Library:

            应用程序可以在这里存储数据。用来存放不想共享给用户的文件,需要时可以创建自己的子目录。

         3)Tmp:

            Tmp目录供应用存储临时文件。在不需要这些文件时,应用要负责删除tmp中等待文件,以免占用文件系统的空间。

    1.2.2 获取目录

         沙盒中有上述的三个目录,获取这三个目录和相应内部的文件非常简单,只需使用C函数NSSearchPathForDirectoriesInDomains。其swift声明如下:

    func NSSearchPathForDirectoriesInDomains(_directory: NSSearchPathDirectory, 
                                            _domainMask: NSSearchPathDomainMask,
                                          _ expandTilde: Bool) -> [String] 

          第一个参数指明要查找的内容,第二参数指明查找的范围,其中返回值说明返回的是数组,但是由于在沙盒中只有一个documents,或是只有一个library,那么返回的数组只有一个元素,我们只需取得第一个元素即可。

    1) 获取Documents目录

    1 func serarchDocumentDirectory() 
    2 { 
    3         let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, 
    4                                                           NSSearchPathDomainMask.UserDomainMask, 
    5                                                                                            true); 
    6         let documentsDirectory = paths[0as String; 
    7         print(documentsDirectory); 
    8 } 

    2) 获取Library目录

    1 func searchLibray() 
    2 { 
    3         let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibraryDirectory, 
    4                                                          NSSearchPathDomainMask.UserDomainMask, 
    5                                                                                          true); 
    6         let libraryDirectory = paths[0as String; 
    7         print(libraryDirectory); 
    8 } 

    3) 获取tmp目录

          获取应用程序中的临时目录的路径,要比获取Documents的目录要容易的多,有一个NSTemporaryDirectory()的函数将返回一个字符串,该字符串包含到应用程序的临时目录的完整路径。

    1 func searchTmp() 
    2 { 
    3       let tmpDirectory = NSTemporaryDirectory(); 
    4       print(tmpDirectory); 
    5  } 

    2. 属性列表

    2.1 功能

           属性列表文件是一种xml文件,Foundation框架中的数组字典都可以与属性列表文件互相转换如图 1所示的转换。简单的说就是调用数组或字典的方法(read或write)进行xml文件的读或写操作

     

     图 1

         虽然可以将数组和字典转换为XML文件,但只有某些对象才能被放置到集合(即数组和字典)中,来实现转换。这些可被放置到集合的类有如下:

    • Array、NSArray、NSMutableArray;
    • Dictionary、NSDictionary、NSMutableDictionary;
    • NSData、NSMutableData;
    • String、NSString、NSMutableString;
    • NSNumber;
    • NSDate。

    2.2 使用

          实现集合和xml文件之间的转换非常简单,只是调用一下集合的写入和读取方法即可。

    表 1

    集合

    方法(Object-C)

    描述

    NSArray

    +arrayWithContentsOfFile(读)

    静态创建工厂方法,用于从属性列表文件中读取数据,创建NSArray对象。Swift没有对应的构造器。

    -initWithContentsOfFile(读)

    构造器,用于从属性列表文件中读取数据,创建NSArray对象。Swift表示为convenience init?(contentsOfFile aPath:String)。

    -writeToFile:atomically(写)

    该方法把NSArray对象写入属性列表文件中。Swift是writeToFile。

    NSDictionary

    +dictionaryWithContentsOfFile(读)

    静态工厂方法,从属性列表文件中读取数据,创建NSDictionary对象。Swift没有对应的构造器。

    -initWithContentsOfFile(读)

    构造器,从属性列表文件中读取数据,创建NSDictionary对象。Swift表示成convenience init?(contentsOfFile aPath:String)。

    -writeToFile:atomically(写)

    将NSDictionary对象写入到属性列表文件中,Swift是writeToFile。

    注意:

             由于Swift代码中的writeToFile(,atomically)方法实际属于ObjectC的NSArray或NSDictionary类。所以要使用这个方法时,需要将Swift的Array(Dictionary)强制转换为NSArray(NSDictionary)。如:

    let nsArray = array as! NSArray 

    2.3 实例

          本实例首先是手动创建一个数组;接着将数组写入属性列表文件(file.txt);然后从属性列表文件中重新读取到数组中;最后输出验证正确性。

    func testPersistence() 

            //1:获取Documents的路径,并创建file.txt的路径 
            let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, 
                                                              NSSearchPathDomainMask.UserDomainMask, 
                                                                                               true); 
            var documentsDirectory = paths[0as String; 
            documentsDirectory = documentsDirectory.stringByAppendingString("/file.txt"); 
     
            //2:创建swift的数组 
            var array:[String] = ["1","2"]; 
     
            //3:将swift的数组转换为ObjectC的数组,并将数组写入属性列表文件 
            let writeArray:NSArray = array as NSArray; 
            writeArray.writeToFile(documentsDirectory, atomically: true); 
     
            //4:从属性列表文件中读取数组 
            let readArray = NSArray(contentsOfFile: documentsDirectory) as! [String]; 
     
            //5:输出验证 
            for var i = 0; i < readArray.count; i++ 
            { 
                print(readArray[i]); 
            } 

    3. 对象归档

    3.1 简介

          归档与属性列表方式不同,属性列表只有指定的一些对象才能进行持久化,而归档是任何实现了NSCopying协议的对象都可以被持久化,其中归档涉及两个类:NSKeyedArchiverNSKeyedUnarchiver。

        同时对于上述三个类的归档和反归档都是采用健值对的形式编码。

    3.2 实现协议

          对于需要被归档化对象,需要实现NSCoding协议,该类只有两方法需要实现且只有两个方法:

    表 2 Object-c语言的NSCoding协议

    方法

    描述

    -(void)encodeWithCoder:(NSCoder *)encoder

    对象进行序列化的方法,把对象信息封装在NSCoder对象中。

    -(instancetype)initWithCoder:(NSCoder *)decoder

    对象的反序列化方法,通过NSCoder对象获取相应数据。

        其中encoder和decoder是提供给用户进行编码和解码的流对象,两个都是采用健值对的形式进行操作,并根据不同的数据类型提供不同的写入和读取的方法,如encodeIntencodeFloatdecodeIntForKeydecodeFloatForKey等方法。

    如下是myObject类实现的两个协议的程序:

     1 @interface myObject : NSObject <NSCoding> 
     2 { 
     3     int age; 
     4     float height; 
     5 } 
     6 @end 
     7  
     8 @implementation myObject 
     9 -(void)encodeWithCoder:(NSCoder *)aCoder 
    10 { 
    11     [aCoder encodeInt:age forKey:@"age"]; 
    12     [aCoder encodeFloat:height forKey:@"height"]; 
    13 } 
    14  
    15 -(instancetype)initWithCoder:(NSCoder *)aDecoder 
    16 { 
    17     self = [super init]; 
    18  
    19     age = [aDecoder decodeIntForKey:@"age"]; 
    20     height = [aDecoder decodeFloatForKey:@"height"]; 
    21     return self; 
    22 } 
    23 @end 

    3.3 归档与反归档

          对需进行持久化和反持久化的对象必须实现NSCoding协议,然后才可以利用NSKeyedArchiverNSKeyedUnarchiver对象进行操作。

    3.3.1 归档

          归档化过程是使用NSKeyedArchiver对象归档数据,其操作步骤如下:

          1) 创建NSMutableData对象:只需使用构造函数init()创建为空的对象;

          2) 创建NSKeyedArchiver对象:用其构造函数initForWritingWithMutableData()创建对象; 

          3) 归档对象:调用NSKeyedArchiver对象的encodeObject()方法写入被归档的对象;

          4) 完成操作:调用NSKeyedArchiver对象的finishEncoding()方法完成写入操作;

          5) 写入文件:调用NSMutableData对象的writeToFile()写入到指定的目录下;

    3.3.2 反归档

          对象反归档的过程与对象归档过程类似,不同的是在创建NSMutableData对象时,需要指定目录路径,且不需要写入文件中。其操作步骤如下:

          1) 创建NSMutableData对象:指定文件路径调用构造函数initWithContentsOfFile()创建对象;

          2) 创建NSKeyedUnarchiver 对象:用其构造函数initForReadingWithData()创建对象;

          3) 反归档对象:调用NSKeyedUnarchiver 对象的decodeObjectForKey()方法写入被归档的对象;

          4) 完成操作:调用NSKeyedUnarchiver 对象的finishEncoding()方法完成写入操作;

    3.4 简单示例

         如对上述的myObject类进行归档化和反归档化的过程为:

     1 - (void)viewDidLoad { 
     2     [super viewDidLoad]; 
     3 //1:获取Documents的路径,并创建file.txt的路径 
     4     NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, true); 
     5     NSString *documentsDirectory = paths[0]; 
     6     documentsDirectory = [documentsDirectory stringByAppendingPathComponent:@"file.txt"]; 
     7 //2:创建被保存的数据 
     8     myObject *mo = [[myObject alloc] init]; 
     9     [mo setAge:122]; 
    10     [mo setHeight:23]; 
    11  
    12 //3:进行数据归档 
    13     NSMutableData *encodeData = [[NSMutableData alloc] init]; 
    14     NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:encodeData]; 
    15     [archiver encodeObject:mo forKey:@"myObject"]; 
    16     [archiver finishEncoding]; 
    17     [encodeData writeToFile:documentsDirectory atomically:true]; 
    18  
    19 //4:进行数据反归档 
    20     NSMutableData *decodeData = [[NSMutableData alloc] initWithContentsOfFile:documentsDirectory]; 
    21     NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:decodeData]; 
    22     myObject *dmo = [unarchiver decodeObjectForKey:@"myObject"]; 
    23 [unarchiver finishDecoding]; 
    24  
    25 //5:验证数据 
    26     NSLog(@"%d %f",[dmo age],[dmo height]); 
    27 } 

    3.5 编码和反编码C语言类型

          NSKeyedArchiver 和NSKeyedUnarchiver类不能对structures, arrays, 和bit fields类型进行编码或反编码。

    3.5.1 指针类型

         由于不能归档指针类型,所以若需要只能归档指针所指向的对象。但对于C语言的字符串类型(char*)却是支持的,它比较特殊,可以使用  encodeBytes:length:forKey:方法进行归档。

    3.5.2 基本数据类型的数组

         一种基本的方法是一个元素一个元素归档,如"theArray[0]", "theArray[1]"这样一个个的进行归档,非常简单。

    3.5.3 对象类型的数组

         对于C语言的数组且元素类型是对象,那么最简单的方式是将该数组用NSArray进行封装。这样就可以进行归档了;当进行反归档时,也是获得NSArray对象,然后一个个地拆封为C语言的数组元素。

    3.5.4 数据结构类型

         可以将结构体的封装为一个对象,对象的每个成员是结构体的每个成员。若需要归档则先将结构体封装为OC对象,然后再归档;而反归档则是将其解析为OC对象,然后转换为C语言结构体。

  • 相关阅读:
    查看 并发请求数及其TCP连接状态【转】
    nohup使用(转)
    Linux下高cpu解决方案(转载)
    Java.lang.String 乱码反编译
    apache2.2 搭载本地中转服务器
    转:对于服务器AdminServer, 与计算机Machine-0相关联的节点管理器无法访问
    感兴趣的github项目
    .NET CORE学习
    使用hMailServer搭建邮件服务器
    记录Sqlserver2012附加Sqlserver2008的数据库出错的解决方案
  • 原文地址:https://www.cnblogs.com/huliangwen/p/5425380.html
Copyright © 2020-2023  润新知