除了固化和NSData,还有其他一些途径可以实现数据的存取。下面介绍其中几种主要途径(Core Data会在第23章介绍)。
编写iOS应用时,也可以使用C语言库的标准I/O函数,代码如下:
FILE *inFile = fopen(“textfile”, “rt”);
char *buffer = malloc(someSize);
fread(buffer, byteCount, 1, inFile);
FILE *outFile = fopen(“binaryfile”, “w”);
fwrite(buffer, byteCount, 1, outFile);
但是在编写iOS应用时,标准I/O函数并不常用。这是因为还有其他的途径可以读/写二进制数据或文本数据,而且更方便。如果要读/写二进制数据,则可以使用NSData。如果要读/写文本数据,则可以分别使用NSString的实例方法initWithContentsOfFile:和writeToFile:atomically:encoding:error:,代码如下:
// 用于保存NSError对象地址的局部指针变量
NSError *err;
NSString *someString = @“Text Data”;
BOOL success = [someString writeToFile:@“/some/path/file”
atomically:YES
encoding:NSUTF8StringEncoding
error:&err];
if (!success) {
NSLog(@“Error writing file: %@”, [err localizedDescription]);
}
NSString *myEssay
= [[NSString alloc] initWithContentsOfFile:@“/some/path/file”
encoding:NSUTF8StringEncoding
error:&err];
if (!myEssay) {
NSLog(@“Error reading file: %@”, [err localizedDescription]);
}
先介绍这段代码中的NSError。应用在执行方法时,可能会因为各种原因而导致失败。例如,将数据写入文件时会因为路径无效而失败,也可能会因为没有足够的权限而失败。NSError对象的作用是保存失败的原因。向NSError对象发送localizedDescription消息,可以得到相应错误的描述信息。因为这些信息是给人看的(human-readable),所以可以向用户显示或通过控制台输出。
在这段代码中,获取NSError对象的语法看上去有些奇怪。通常情况下,只有当某个方法在执行代码时发生了错误,才有必要创建NSError对象。也就是说,负责创建NSError对象的应该是被调用的方法,而不是调用方。为了能让调用方得到被调用的方法所创建的NSError对象,需要将指针变量的地址(&err)作为实参传给相应的方法。假设有某个方法(方法A),该方法的某个实参可以返回一个NSError对象,那么在调用方法A前,需要先创建一个类型为NSError *的指针变量(局域变量)。注意,这里不需要创建NSError对象,因为这是方法A的任务。然后将这个局部变量的地址(&err)传给可能会出错的方法A。如果方法A在执行代码时发生了错误,就会创建一个NSError对象,并将新创建的对象的地址赋给传入的指针。如果调用方不关心NSError对象,则可以传入nil。
如果要向用户显示错误,通常可以使用UIAlertView对象(见图18-10)。
图18-10 UIAlertView示例
可以使用如下代码创建并显示UIAlertView:
NSString *myEssay
= [[NSString alloc] initWithContentsOfFile:@“/some/path/file”
encoding:NSUTF8StringEncoding
error:&err];
if (!myEssay) {
UIAlertView *a
= [[UIAlertView alloc] initWithTitle:@“Read Failed”
message:[err localizedDescription]
delegate:nil
cancelButtonTitle:@“OK”
otherButtonTitles:nil];
[a show];
}
很多语言将所有非预期(unexpected)错误作为异常抛出,但是Objective-C的异常只用来处理程序错误。当异常抛出时,详细信息都封装在NSException对象中。这些信息主要用来帮助程序员调试代码,例如“试图在只有两个对象的数组中访问第七个对象。”NSException中还包括方法调用栈信息,指明了抛出异常的代码位置。
NSException和NSError的使用场景不同。如果需要指出程序员的编码错误,则应该使用NSException。例如,一个方法只能接受奇数作为参数,但是程序员在调用该方法时传入了偶数,这时应该抛出异常,以方便程序员解决代码错误。相反,对于预期(expected)错误,如用户错误和设备环境错误,应该使用NSError。例如,一个方法需要读取用户照片,但是没有访问用户相册的权限,这时应该向方法调用者返回一个NSError对象,指出不能执行本次操作的原因。
现在继续讨论文件读/写。和NSString类似,NSDictionary和NSArray也有writeToFile:和initWithContentsOfFile:。只有当collection对象包含可序列化(property list serializable)对象时,才能通过writeToFile:这类方法将数据存入文件。可序列化对象包括NSString、NSNumber、NSDate、NSData、NSArray和NSDictionary。NSArray对象或NSDictionary对象这类文件的写入方法生成的都是XML格式的property list文件,代码如下:
<?xml version=“1.0” encoding=“UTF-8”?>
<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN”
“http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
<plist version=“1.0”>
<array>
<dict>
<key>firstName</key>
<string>Christian</string>
<key>lastName</key>
<string>Keur</string>
</dict>
<dict>
<key>firstName</key>
<string>Joe</string>
<key>lastName</key>
<string>Conway</string>
</dict>
<dict>
<key>firstName</key>
<string>Aaron</string>
<key>lastName</key>
<string>Hillegass</string>
</dict>
</array>
</plist>
几乎所有的操作系统都能读取XML格式的property list文件,所以使用这种格式存储数据会很方便。很多Web服务程序会采用这种格式的文件作为输入和输出。读/写property list文件的代码如下:
NSMutableDictionary *d = [NSMutableDictionary dictionary];
d[@“String”] = @“A string”;
[d writeToFile:@“/some/path/file” atomically:YES];
NSMutableDictionary *anotherD = [[NSMutableDictionary alloc]
initWithContentsOfFile:@“/some/path/file”];