2018-05-30 文件加载与保存

一.属性列表

在Cocoa中,有一类名为属性列表的对象,常简写为plist。这些列表包含Cocoa知道如何操作的一组对象,即Cocoa知道如何将他们保存到文件中并进行加载。包括NSArray、NSDictionary、NSString、NSNumber、NSDate、NSData,以及他们的变体。

1.NSDate

使用[NSDate date]获得当前的日期和时间。

NSDate *date = [NSDate date];
NSLog(@"%@", date);

当然,可以获取任何时间,只要你知道那时候距离现在有多久。对于将来的时间用的时间间隔,对于过去的时间用的时间间隔。

NSDate *yesterday = [NSDate dateWithTimeIntervalSinceNow: -(24 * 60 * 60)];
NSLog(@"%@", yesterday);
NSDate *tomorrow = [NSDate dateWithTimeIntervalSinceNow: (24 * 60 * 60)];
NSLog(@"%@", tomorrow);

2.NSData

NSData包装了大量字节,我们可以获得数据的长度和指向字节起始位置的一个指针。如果将数据块传递给一个函数或方法,可以通过传递一个自动释放的NSData来实现,无需担心内存清除问题。

//char* 转 NSData
const char * string = "Hi, I am Hoben Wong.";
NSData *data = [NSData dataWithBytes: string length:strlen(string) + 1];
//NSString 转 data
NSString *string_NS = @"Hi, I am Hoben Wong.";
NSData *data_NS = [string_NS dataUsingEncoding: NSUTF8StringEncoding];
//NSData 转 char*
NSLog(@"%s's length is %d", [data bytes], (int)[data length]);
//NSData 转 NSString
NSString *result = [[NSString alloc] initWithData: data_NS encoding:NSUTF8StringEncoding];
NSLog(@"%@'s length is %d", result, (int)[result length]);

二.编码、解码与归档

1.为什么要用编码?

  • Size过大
  • 包含隐私数据
  • URL本身而言,歧义...
    对于最后一种比较常见,例如服务器通过截取& = 来区分你的key 和value。一旦你的参数中包含了&=则就会分割错误引起歧义从而需要编码。

2.编码与解码

采用NSCoding协议,可以使用自己的对象实现编解码功能。

//NSCoding.h
#import 

@protocol NSCoding

-(void) encodeWithCoder: (NSCoder *) aCoder;
-(void) initWithCoder: (NSCoder *) aDecoder;

@end

通过采用该协议,可以实现两种功能:对象需要保存自身、对象需要加载自身。
首先,在Thingie.h中实现NSCoder协议,并声明对象内容:

//Thingie.h
#import 

@interface Thingie : NSObject 
{
    NSString *name;
    int magicNumber;
    float shoeSize;
    NSMutableArray *subThingies;
}

@property (copy) NSString *name;
@property int magicNumber;
@property float shoeSize;
@property NSMutableArray *subThingies;

-(id) initWithName: (NSString *) n
        magicNumer: (int) mn
          shoeSize: (float) ss;

@end

然后在Thingie.m中实现协议和.h文件定义的方法:

//Thingie.m
-(void) encodeWithCoder:(NSCoder *) coder
{
    [coder encodeObject: name forKey: @"name"];
    [coder encodeInt: magicNumber forKey: @"magicNumber"];
    [coder encodeFloat: shoeSize forKey: @"shoeSize"];
    [coder encodeObject: subThingies forKey: @"subThingies"];
}

-(id) initWithCoder:(NSCoder *) decoder
{
    if (self = [super init]) {
        self.name = [decoder decodeObjectForKey: @"name"];
        self.magicNumber = [decoder decodeIntForKey: @"magicNumber"];
        self.shoeSize = [decoder decodeFloatForKey: @"shoeSize"];
        self.subThingies = [decoder decodeObjectForKey: @"subThingies"];
    }
    return self;
}

最后在main.m文件初始化Thingie

Thingie *thing1;
thing1 = [[Thingie alloc] initWithName: @"Thing1" magicNumer: 42 shoeSize: 10.5];
Thingie *another;
another = [[Thingie alloc] initWithName: @"Thing2" magicNumer: 23 shoeSize: 13.0];
[thing1.subThingies addObject: another];
another = [[Thingie alloc] initWithName: @"Thing3" magicNumer: 17 shoeSize: 9.0];
[thing1.subThingies addObject: another];
NSLog(@"%@", thing1);

3.归档与解压

NSCoder是一个抽象类,定义一些有用的方法来在对象与NSData之间来回转换。我们使用NSCoder的两个子类NSKeyedArchiver和NSKeyedUnarchiver来对对象进行归档和解压。

//归档
NSData *freezeDried;
freezeDried = [NSKeyedArchiver archivedDataWithRootObject: thing1];

archivedDataWithRootObject方法编码thing1对象。过程如下:

  • 首先,它在后台创建一个NSKeyedArchiver实例
  • 然后,它将NSKeyedArchiver实例传递给对象thing1的-encodeWithCoder方法。
  • 当thing1编码自身的属性时,它可能对其他对象进行编码,例如字符串、数组以及我们可能输入到该数组中的任何内容。
  • 整个对象集合完成对键和值的编码后,具有键值对的归档程序将所有对象扁平化为一个NSData类并将其返回。
//解压
Thingie *thing2 = [NSKeyedUnarchiver unarchiveObjectWithData: freezeDried];
NSLog(@"%@", thing2);

三.总结

可以采用NSCoding协议和实现方法来编码和解码对象:可以将自己的大量对象转换成NSData类,然后将它保存到磁盘中,并可以在以后读取它。通过这种NSData类,可以重新创建对象。

你可能感兴趣的:(2018-05-30 文件加载与保存)