Objective-C & Sprite Kit太空历险记 : 9. 冷冻生化战士——归档

原文

  http://www.ituring.com.cn/article/214104

    

在太空军事学院里,还有一个军事生化技术研究中心,这一天,突然来了几艘医疗太空船,据了解,这些太空船带来很多受来外星生化袭击的太空舰队人员;而他们来到这里的目的很简单,通过医疗和生物改造而活下去。而这些工作则是由A博士主持的,他的军衔为中校。

A博士:这些受伤的人都是通过冷冻后运送过来,否则他们将不会存活;在了解更多的技术之前,我们先了解如何进人员的冷冻和解冻,前提是,要活的!

在Objective-C项目中,我们可以将对象归档至文件,然后可以通过解档操作快速恢复它们;此外,我们还可以利用归档快速完全复制(深复制)对象。本章,我们就来讨论这些内容。

9.1. 归档与解档

A博士:我们的冷冻技术主要使用了NSKeyedArchiver类和NSKeyedUnarchiver类,它们分别用于归档和解档,而档案的形式可以是NSData对象、NSMutableData对象或文件。只是有一点需要注意,归档和解档操作的类必须实现了NSCoding协议。

9.1.1. 实现NSCoding协议

A博士,接下来,我们会对受到外星生化攻击的士兵进行改造,创建CBionicle类型生化战士;要知道,让他完全恢复是不可能的,所以,这也是唯一让他们活下去的办法了。在这里,CBionicle类会实现NSCoding协议,下面就是接口部分(CBionicle.h文件)。

#ifndef __CBionicle_h__
#define __CBionicle_h__

#import <Foundation/Foundation.h>

@interface CBionicle : NSObject <NSCoding>

@property NSString *name;
@property NSPoint position;
@property int life;
-(void) fire;

@end
#endif

接下来是CBionicle类的实现部分。请注意,在实现部分,除了接口中定义的成员,我们还必须实现initWithCoder:方法和encodeWithCoder:方法,它们都是在NSCoding协议中定义的。如下面的代码(CBionicle.m文件)。

#import "CBionicle.h"

@implementation CBionicle

@synthesize name, position, life;

-(void) fire
{
    NSLog(@"%@ 正在开火!", self.name);
}
// NSCoding协议方法
//
-(void) encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.name forKey:@"CBionicle_name"];
    [aCoder encodePoint:self.position forKey:@"CBionicle_position"];
    [aCoder encodeInt:self.life forKey:@"CBionicle_life"];
}

//
-(id) initWithCoder:(NSCoder *)aDecoder
{
    self.name = [aDecoder decodeObjectForKey:@"CBionicle_name"];
    self.position = [aDecoder decodePointForKey:@"CBionicle_position"];
    self.life = [aDecoder decodeIntForKey:@"CBionicle_life"];
    return self;
}

@end

A博士:我们可以看到NSCoder类中定义的按键(Key)编码和解码的方法,这些方法可以帮助我们对各种类型的数据进行编码和解码,以便进行归档和解档操作。常用的方法包括:

  • encodeObject:forKey:方法和decodeObject:forKey:方法,用于对各种对象进行编码和解码操作。
  • encodeBool:forKey:方法和decodeBool:forKey:方法,对BOOL类型数据进行编码和解码操作。
  • encodeInt:forKey:方法和decodeInt:forKey:方法,对int类型数据进行编码和解码操作。
  • encodeInteger:forKey:方法和decodeInteger:forKey:方法,对NSInteger类型数据进行编码和解码操作。
  • encodeFloat:forKey:方法和decodeFloat:forKey:方法,对float类型数据进行编码和解码操作。
  • encodeDouble:forKey:方法和decodeDouble:forKey:方法,对double类型数据进行编码和解码操作。
  • encodePoint:forKey:方法和decodePoint:forKey:方法,对NSPoint结构数据进行编码和解码操作。
  • encodeSize:forKey:方法和decodeSize:forKey:方法,对NSSize结构数据进行编码和解码操作。
  • encodeRect:forKey:方法和decodeRect:forKey:方法,对NSRect结构数据进行编码和解码操作。

9.1.2. 使用NSKeyedArchiver类和NSKeyedUnarchiver类

A博士:我们创建的CBionicle类已经实现了NSCoding协议,接下来,我们就可以对这个类型的对象,也就是具体的生化士兵进行冷冻(归档)和解冻(解档)操作了。首先,我们看一看如何进行归档操作,如下面的代码。

NSArray *paths = NSSearchPathForDirectoriesInDomains(
     NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = paths.firstObject;
//  归档文件
NSString *filename =
    [docPath stringByAppendingPathComponent:@"SoldierSmith"];
//
CBionicle *soldier = [[CBionicle alloc] init];
soldier.position = CGMakePoint(10, 15);
soldier.name = @"Smith";
NSLog(@"%i", [NSKeyedArchiver archiveRootObject:soldier toFile:filename]);

我们可以在main()函数中测试这些代码,请注意,不要忘记使用#import指令引用"CBionicle.h"文件。代码成功执行后,会在“文稿”目录中生成一个名为SoldierSmith的文件。

A博士:现在,Smith已经冷冻到“文稿”目录了,下面的代码,我们将利用NSKeyedUnarchiver类对其进行解冻(解档)操作。

NSArray *paths = NSSearchPathForDirectoriesInDomains(
    NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = paths.firstObject;
//  归档文件
NSString *filename =
    [docPath stringByAppendingPathComponent:@"SoldierSmith"];
//
CBionicle *soldier = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];
NSLog(@"%@", soldier.name);
[soldier fire];

A博士:通过实现NSCoding协议,以及归档操作,我们可以发现自己定义的类也可以很方便地进行持久化,如将对象数据保存到磁盘文件。然后,我们可通过解档操作方便、快捷地恢复对象。

在Foundation框架和其它开发资源中,我们同样可以通过归档和解档对已经实现了NSCoding协议的对象进行持久化和恢复操作。此外,有些类型已经定义了将对象数据写入文件和读取的方法,我们可以很方便地对它们进行归档和解档操作,如NSArray等。

9.2. 克隆生化战士(利用归档复制对象)

A博士:在实际应用中,虽然生化士兵损失不小,但其战斗力还是很强大的,所以,在军事生化技术研究中心,我们又想到了一个办法创造更多的生化战士,就是利用快速克隆技术进行克隆。这就是归档的另一个功能,即快速地复制一个对象,请注意,这里是指对象的深复制操作。

如果类型实现了NSCopying协议,则可以直接使用clone方法进行深复制操作;不过,如果类型没有实现NSCopying协议,但不小心实现了NSCoding协议,那么,我们就可以使用归档的方法进行这类对象的深复制操作,此时,我们需要NSData对象作用“中间人”。

如下面的代码,我们将对士兵Smith进行克隆(深复制),请注意,CBionicle类并没有实现NSCopying协议。

NSArray *paths = NSSearchPathForDirectoriesInDomains(
    NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = paths.firstObject;
//  归档文件
NSString *filename =
    [docPath stringByAppendingPathComponent:@"SoldierSmith"];
//
CBionicle *soldier = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:soldier];
// 开始克隆10个Smith
CBionicle *soldiers[10];
for (int i=0; i < 10 ; i++)
{
    soldiers[i] = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    soldiers[i].name =  [NSString stringWithFormat:@"Smith-%i", i+1];
}
// 全体开火
for (int i=0; i < 10 ; i++)
{
    [soldiers[i] fire];
}

9.3. 游戏存档

A博士:我们还没开始开发游戏呢,怎么就开始存档了?要知道,已经越来越接近太空舰队远征的日期了,而在旅行或挑战中能够时刻保存一些数据当然是有必要的,我想,L上尉也会有这样的想法,这样,她的图书馆又会有更多的资源了。

实际上,当我们开始讨论游戏的开发以后,大家就会发现,在游戏中需要保存的数据类型及保存方法实际上已经讨论过了;在游戏中,我们使用的数据类型更多的会是int、NSInteger、float、CGFloat、CGPoint、CGSize等一些常用的数据或结构,而保存这些数据时,我们可以使用NSMutableArray对象组织这些数据,然后,通过writeToFile::方法直接保存到存档文件;当然,这个存档文件可能在应用的“沙盒(sandbox)”里,也可能在用户的文稿目录中。

这只是一个基本的保存游戏数据的方法,在实际的项目中,我们需要根据数据量的大小,以及格式要求灵活地使用保存策略,甚至,对于大量的数据,我们还可以使用数据库,如CoreData、SQLite等。

A博士:听说大家就要启航了,祝各位在太空里好运! 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值