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

来源:互联网 发布:板报排版 预防网络诈骗 编辑:程序博客网 时间:2024/05/17 02:46
原文

  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个SmithCBionicle *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博士:听说大家就要启航了,祝各位在太空里好运! 

 
0 0