【IOS 开发学习总结-OC-37】★文件 I/O——对象归档

何为对象归档?

对象归档——就是用某种格式把一个或多个对象保存到指定文件中,方便以后从文件中恢复它们。类似于其他语言中的序列化机制。
归档包括2方面的操作:1,将对象写入指定文件;2,从文件中恢复这些对象。

使用NSKeyedArchiver&NSKeyedUnarchiver归档和解档

使用NSKeyedArchiver将对象进行归档,若要恢复这些对象,要用NSKeyedUnarchiver。
NSKeyedArchiver会创建一种所谓带键(keyed) 的档案,这种归档格式中,无论归档哪个对象,都要为该对象分配一个 key, 当程序进行恢复这些对象时,也需要根据 key进行检索。

NSKeyedArchiver&NSKeyedUnarchiver的用法:

用法有如下2种方式:

  1. 归档时:直接调用NSKeyedArchiver的+ (NSData *)archivedDataWithRootObject:(id)rootObject;+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
    类方法将指定对象作为root 进行归档;
    解档恢复时:调用NSKeyedUnarchiver 的+ (nullable id)unarchiveObjectWithData:(NSData *)data;+ (nullable id)unarchiveObjectWithFile:(NSString *)path;类方法。
  2. 归档时: 以NSMutableData对象作为参数,创建NSKeyedArchiver对象,然后重复调用NSKeyedArchiver对象的- (void)encodeObject:(nullable id)objv forKey:(NSString *)key;此类方法依次归档不同的对象,最后调用- (void)finishEncoding;方法结束归档;
    解档恢复时:依然以NSMutableData对象作为参数,创建NSKeyedUnarchiver对象,然后重复调用- (nullable id)decodeObjectForKey:(NSString *)key;方法依次恢复不同的对象。

第一种方式示例代码:
归档操作:

#import <Foundation/Foundation.h>

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 直接使用多个value,key的形式创建NSDictionary对象
        NSDictionary* dict = [NSDictionary 
            dictionaryWithObjectsAndKeys:
            [NSNumber numberWithInt:89] , @"Objective-C",
            [NSNumber numberWithInt:69] , @"Ruby",
            [NSNumber numberWithInt:75] , @"Python",
            [NSNumber numberWithInt:109] , @"Perl", nil];
        // 对dict对象进行归档      
        [NSKeyedArchiver archiveRootObject:dict
            toFile:@"myDict.archive"];
    }
}

解档恢复操作:

#import <Foundation/Foundation.h>

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 从myDict.archive文件中恢复对象
        NSDictionary* dict = [NSKeyedUnarchiver
            unarchiveObjectWithFile:@"myDict.archive"];
        // 下面代码只是获取NSDictionary中key-value数据
        NSLog(@"Objective-C对应的value:%@" ,
            [dict valueForKey:@"Objective-C"]);
        NSLog(@"Ruby对应的value:%@" ,
            [dict valueForKey:@"Ruby"]);
        NSLog(@"Python对应的value:%@" ,
            [dict valueForKey:@"Python"]);
        NSLog(@"Perl对应的value:%@" ,
            [dict valueForKey:@"Perl"]);
    }
}

代码说明:这种方式简单易用,但功能简单。注意:这里归档的myDict.archive文件(文件名可以任意指定)。

第二种归档/解档方式

跳转到:使用 NSData 完成自定义归档

实现 NSCoding协议

NSKeyedArchiver和NSKeyedUnarchiver这2个类,能否归档任意自定义的 OC 对象呢?
如果我们用NSKeyedArchiver将一个自定义的 FKApple 对象归档到指定文件中,运行会报错如下:
这里写图片描述
从报错信息中,我们可以看出,我们归档自定义对象时,系统会自动调用自定义对象的encodeWithCoder: 方法进行归档,类似的在解档恢复操作时,会调用该对象的decodeWithCoder: 方法 进行恢复。

那么问题来了:我们怎么实现对任意对象的归档呢?
如果程序需要归档,恢复任意自定义类的实例,那么该类需要实现NSCoding协议,实现该协议就必须实现该协议中定义的如下2个方法:

  1. initWithCoder:——负责恢复该对象;
  2. encodeWithCoder: ——负责归档该对象

一般来说,如果该对象 得到实例变量是 objective-c 类型 ,并且实现了NSCoding协议,就可以直接调用(void)encodeObject:(nullable id)objv forKey:(NSString *)key;方法来归档该实例变量,使用
- (nullable id)decodeObjectForKey:(NSString *)key;方法恢复该实例变量即可。

C 语言的基本数据类型如何归档和解档恢复?

对C语言的数据类型(如整型或浮点型),可用如下 表中的方法进行归档或恢复。
这里写图片描述

示例代码:
FKApple.h

#import <Foundation/Foundation.h>

// 定义FKApple类,实现NSCoding协议
@interface FKApple : NSObject <NSCoding>
@property (nonatomic , copy) NSString* color;
@property (nonatomic , assign) double weight;
@property (nonatomic , assign) int size;
- (id) initWithColor: (NSString*) color
    weight: (double) weight size: (int) size;
@end

FKApple.m

#import "FKApple.h"

@implementation FKApple
@synthesize color = _color;
@synthesize weight = _weight;
@synthesize size = _size;
- (id) initWithColor: (NSString*) color 
    weight:(double) weight size:(int) size
{
    if(self = [super init])
    {
        self.color = color;
        self.weight = weight;
        self.size = size;
    }
    return self;
}
// 重写父类的decription方法
- (NSString*) description
{
    // 返回一个字符串
    return [NSString stringWithFormat:
        @"<FKApple[_color=%@, _weight=%g, _size=%d]>"
        , self.color , self.weight , self.size];
}
- (void) encodeWithCoder: (NSCoder*) coder
{
    // 调用NSCoder的方法归档该对象的每个实例变量
    [coder encodeObject:_color forKey:@"color"];
    [coder encodeDouble:_weight forKey:@"weight"];
    [coder encodeInt:_size forKey:@"size"];
}
- (id) initWithCoder: (NSCoder*) coder
{
    // 使用NSCoder依次恢复color、weight、size这3个key
    // 所对应的value,并将恢复的value赋给当前对象的3个实例变量
    _color = [coder decodeObjectForKey:@"color"];
    _weight = [coder decodeDoubleForKey:@"weight"];
    _size = [coder decodeIntForKey:@"size"];
    return self;
}
@end

ArchiverApple.m

#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 创建FKApple对象
        FKApple* apple = [[FKApple alloc]
            initWithColor:@"红色"
            weight:3.4
            size:20];
        // 对apple对象进行归档     
        [NSKeyedArchiver archiveRootObject:apple
            toFile:@"apple.archive"];
    }
}

另外,解档恢复的.m 文件。

UnarchiverApple.m

#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 从归档文件中恢复对象   
        FKApple* apple = [NSKeyedUnarchiver 
            unarchiveObjectWithFile:@"apple.archive"];
        // 获取对象的属性
        NSLog(@"苹果的颜色为:%@" , apple.color);
        NSLog(@"苹果的重量为:%g" , apple.weight);
        NSLog(@"苹果的规格为:%d" , apple.size);
    }
}

代码说明:
上面的代码中实现了NSCoding协议,使用NSKeyedArchiver归档普通 OC 对象与归档 NSDictionary 对象的方式一样。解档恢复方式也是一样的。
 

使用 NSData 完成自定义归档

如果我们希望一次性收集多个对象,并将这些对象归档到单个档案文件中,此时我们需要借助NSMutableData 来创建NSKeyedArchiver或NSKeyedUnarchiver 对象。

程序一次性将多个对象归档到单个档案文件中的步骤

步骤如下:

  1. 以NSMutableData为参数,创建NSKeyedArchiver对象;
  2. 重复调用NSKeyedArchiver对象的- (void)encodeObject:(nullable id)objv forKey:(NSString *)key;此类方法,用来归档所有需要归档到 一个文件中的对象。
  3. 结束归档。——调用- (void)finishEncoding;方法结束归档;
  4. 根据需要,可以选择将保存归档 数据的 NSData 通过网络传输或输出到磁盘文件上。

示例代码(一次性归档多个 对象):

#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 直接使用多个value,key的形式创建NSDictionary对象
        NSDictionary* dict = [NSDictionary 
            dictionaryWithObjectsAndKeys:
            [NSNumber numberWithInt:89] , @"Objective-C",
            [NSNumber numberWithInt:69] , @"Ruby",
            [NSNumber numberWithInt:75] , @"Python",
            [NSNumber numberWithInt:109] , @"Perl", nil];
        // 创建一个NSSet对象      
        NSSet* set = [NSSet setWithObjects:
            @"疯狂iOS讲义",
            @"疯狂Android讲义",
            @"疯狂Ajax讲义", nil];
        // 创建FKApple对象
        FKApple* apple = [[FKApple alloc]
            initWithColor:@"红色"
            weight:3.4
            size:20];
        // 创建一个NSMutableData对象,用于保存归档数据
        NSMutableData* data = [NSMutableData data];
        // 以NSMutableData对象作为参数,创建NSKeyedArchiver对象
        NSKeyedArchiver* arch = [[NSKeyedArchiver alloc]
            initForWritingWithMutableData:data];
        // 重复调用encodeObject:forKey:方法归档所有需要归档的对象
        [arch encodeObject:dict forKey:@"myDict"];
        [arch encodeObject:set forKey:@"set"];
        [arch encodeObject:apple forKey:@"myApp"];
        // 结束归档
        [arch finishEncoding];
        // 程序将NSData缓存区保存的数据写入文件
        if([data writeToFile:@"multi.archive" atomically:YES] == NO)
        {
            NSLog(@"归档失败!");
        }
    }
}
#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 直接使用多个value,key的形式创建NSDictionary对象
        NSDictionary* dict = [NSDictionary 
            dictionaryWithObjectsAndKeys:
            [NSNumber numberWithInt:89] , @"Objective-C",
            [NSNumber numberWithInt:69] , @"Ruby",
            [NSNumber numberWithInt:75] , @"Python",
            [NSNumber numberWithInt:109] , @"Perl", nil];
        // 创建一个NSSet对象      
        NSSet* set = [NSSet setWithObjects:
            @"疯狂iOS讲义",
            @"疯狂Android讲义",
            @"疯狂Ajax讲义", nil];
        // 创建FKApple对象
        FKApple* apple = [[FKApple alloc]
            initWithColor:@"红色"
            weight:3.4
            size:20];
        // 创建一个NSMutableData对象,用于保存归档数据
        NSMutableData* data = [NSMutableData data];
        // 以NSMutableData对象作为参数,创建NSKeyedArchiver对象
        NSKeyedArchiver* arch = [[NSKeyedArchiver alloc]
            initForWritingWithMutableData:data];
        // 重复调用encodeObject:forKey:方法归档所有需要归档的对象
        [arch encodeObject:dict forKey:@"myDict"];
        [arch encodeObject:set forKey:@"set"];
        [arch encodeObject:apple forKey:@"myApp"];
        // 结束归档
        [arch finishEncoding];
        // 程序将NSData缓存区保存的数据写入文件
        if([data writeToFile:@"multi.archive" atomically:YES] == NO)
        {
            NSLog(@"归档失败!");
        }
    }
}

从一个文档中 恢复多个归档对象的步骤(与 归档相反)如下:

  1. 以 NSData 作为参数,创建NSKeyedUnarchiver对象;
  2. 重复调用NSKeyedUnarchiver对象的- (nullable id)decodeObjectForKey:(NSString *)key;这类方法从一个文件中恢复所有归档过的对象;
  3. 调用NSKeyedUnarchiver对象的finishDecoding方法结束恢复。

示例代码:

#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 创建一个NSData对象,用于读取指定文件中的归档数据
        NSData* data = [NSData
            dataWithContentsOfFile:@"multi.archive"];
        // 以NSData对象作为参数,创建NSKeyedUnarchiver对象
        NSKeyedUnarchiver* unarch = [[NSKeyedUnarchiver alloc]
            initForReadingWithData:data];
        // 重复调用decodeObjectForKey:方法恢复所有需要恢复的对象
        NSDictionary* dict = [unarch decodeObjectForKey:@"myDict"];
        NSSet* set = [unarch decodeObjectForKey:@"set"];
        FKApple* myApp = [unarch decodeObjectForKey:@"myApp"];
        // 结束恢复
        [unarch finishDecoding];
        // 下面代码仅仅只是验证恢复是否成功
        NSLog(@"%@" , dict);
        NSLog(@"%@" , set);
        NSLog(@"%@" , myApp);
    }
}

这里特别提一嗓子,借助 NSData 的支持,NSKeyedUnarchiver可以一次性将多个对象归档到NSMutableData对象中。由于NSData和NSMutableData代表数据的缓冲区,接下来程序就可以将相应的数据写入网络传输,或者写入底层文件。

使用归档实现深复制

归档会将整个对象转换为字节数据。——特别是,包括这个对象的所有实例变量,如果该实例变量指向另一个 OC 对象,归档时也 会归档该实例变量所指向的 OC对象。
这就是说,当程序归档一个对象时,系统会把该对象关联的所有数据都转为字节数据。如果从这些字节数据中恢复对象,恢复出来的对象与原对象完全相同,,但没有任何共用部分——这就实现了深复制

示例代码:

#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 直接使用多个value,key的形式创建NSDictionary对象
        NSDictionary* dict = [NSDictionary 
            dictionaryWithObjectsAndKeys:
            [[FKApple alloc]
                initWithColor:@"红色"
                weight:3.4
                size:20] , @"one",
            [[FKApple alloc]
                initWithColor:@"绿色"
                weight:2.8
                size:14] , @"two", nil];
        // 归档对象,将归档对象的数据写入NSData中
        NSData* data = [NSKeyedArchiver
            archivedDataWithRootObject:dict];
        // 从NSData对象中恢复对象,这样即可完成深复制
        NSDictionary* dictCopy = [NSKeyedUnarchiver
            unarchiveObjectWithData:data];
        // 获取复制的NSDictionary对象中key为one对应的FKApple对象
        FKApple* app = [dictCopy objectForKey:@"one"];
        // 修改该FKApple对象的color。
        [app setColor:@"紫色"];
        // 获取原始的NSDictionary对象中key为one对应的FKApple对象
        FKApple* oneApp = [dict objectForKey:@"one"];
        // 访问该FKApple的颜色,程序将会发现该颜色没有任何改变
        NSLog(@"dict中key为one对应苹果的颜色为:%@" ,
            oneApp.color);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值