iOS6 CookBook 第12章_文件和文件夹管理


iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 1 版本 1.0 | 2013 年 03 月 17 日 iOS 6 Programming Cookbook 第 12 章 文件和文件夹管理 版本 1.0 翻译时间:2013-03-31 DevDiv 热心网友自发组织翻译 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 2 版本 1.0 | 2013 年 03 月 17 日 写在前面 iOS 6 Programming Cookbook 是 O’Reilly 出版社出版,作者 是 Vandad Nahavandipoor。在这里希望大家尊重原创,尊重知识版 权,此次翻译是 DevDiv 热情网友感觉此书还行,便自发组织翻译, 并无偿分享给广大 iOS 开发者。内容仅供交流学习使用,切勿商用或 者其它一切用途。 如果你觉得内容可以的话,请主动到官网进行购买: http://oreilly.com/ 严重警告:限下载后 24 小时内删除 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 3 版本 1.0 | 2013 年 03 月 17 日 目录 写在前面 2 目录 3 第 12 章 文件和文件夹管理 4 1.0. 介绍 4 1.1. 获得磁盘上最常用文件夹的路径 6 1.1.1. 问题 6 1.1.2. 解决方案 6 1.1.3. 讨论 6 1.1.4. 参考 7 1.2. 对文件进行读写操作 7 1.2.1. 问题 7 1.2.2. 方案 7 1.2.3. 讨论 8 1.2.4. 参考 11 1.3. 在磁盘中创建一个文件夹 11 1.3.1. 问题 11 1.3.2. 方案 11 1.3.3. 讨论 11 1.3.4. 参考 12 1.4. 枚举文件和文件夹 12 1.4.1. 问题 12 1.4.2. 方案 12 1.4.3. 讨论 12 1.4.4. 参考 16 1.5. 删除文件和文件夹 16 1.5.1. 问题 16 1.5.2. 方案 16 1.5.3. 讨论 16 1.5.4. 参考 19 1.6. 磁盘中文件的安全处理 19 1.6.1. 问题 19 1.6.2. 方案 19 1.6.3. 讨论 20 1.6.4. 参考 23 1.7. 将 Object 保存到文件中 23 1.7.1. 问题 23 1.7.2. 方案 24 1.7.3. 讨论 24 1.7.4. 参考 26 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 4 版本 1.0 | 2013 年 03 月 17 日 第 12 章 文件和文件夹管理 1.0. 介绍 IOS 操作系统是基于 Mac OS X 发展而来,而 Mac OS X 自身又是基于 UNIX 操作系统发 展而来.在 IOS 中,操作系统完整的目录结构对具体的应用是不可见的,这些由应用开发者 开发的应用将运行在自己独立的沙箱中.沙箱环境的准确定义应该是这样:只有拥有文 件夹所有权的应用才能对该文件夹中所含内容进行存取的一个授权访问区域.每个应用 都拥有它自己的沙箱文件夹,且默认地,应用能访问沙箱文件夹下的子文件夹. 当一个 IOS 应用被安装到设备上时,系统将为应用创建如图 12-1 所示的文件夹结构. 每个应用的根文件夹中将包含各种子文件,我将在此处解释这些子文件夹: Name.app 尽管对以 name 命名,以.app 为扩展名定义的文件实际为一个文件夹理解上较困难, 但 与你应用的资源束(bundle)有关的主要内容将存放在此.例如:在 IOS 系统安装应用 到设备时,应用所有的图标,二进制主体,不同主题(branding)对应的图片,字体,声音等 等,最将会被自动放在此文件夹下.为应用设置的名称即为产品名称,如你的应用名称为 MyApp,则.app 文件夹的名称将被命名为 MyApp.app. 文档/ 用户创建的内容将存放于此文件夹中.而应用 populated,下载,创建的内容不应该存储在 此文件夹下. Library/ 此文件夹用于存储缓存文件,用户偏好设置等等.通常情况,此文件夹自身不包含任何文 件,而只包含其他含有文件的文件夹. iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 5 版本 1.0 | 2013 年 03 月 17 日 图 12-1. iOS 文件系统描述 Library/Caches/ 此文件用于存储那些需要及可延迟或重创建的临时数据.且这些内容不会被 IOS 系统 备份,特别地,当设备磁盘空间不足且应用不在运行状态时,IOS 系统可能会移除此文件 夹中的内容!所以,不要让你的应用太依赖此文件夹中的内容;而是要时刻准备再次创 建这些内容.再次申明:此文件夹下的内容将不会被 IOS 系统备份,且当你的应用处于 暂停状态时,这些内容可能会被移除. 例如:如果你的应用依赖于在设备磁盘上创建的文件及文件夹,此文件夹可能不是最 好的数据存放地.你最好把这些文件及文件夹存放到 /tmp 文件夹中. Library/Preferences/ 如同文件夹名称所指示的那样,此文件夹包含应用运行时需要保存的参数.我们将在后 面详细讨论这个问题的细节.IOS 系统同样不会备份这个文件夹中的内容. Library/Application Support/ 应用创建的数据,但不包括用户创建的数据,必须保存在此文件夹中.非常幸运,IOS 系统 会备份此文件夹中的内容.此文件夹不会自动创建,如果不存在,你需要自己来创建它.我 们将在本章中讨论此文件夹的创建. iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 6 版本 1.0 | 2013 年 03 月 17 日 tmp/ 此文件夹包含应用创建,下载等产生的临时文件.IOS 系统不会备份此文件夹中的内容. 例如:你可能会为了提升应用的性能而把从互联网上下载的一批相片存放在此文件夹中, 当用户再次打开应用时,就不用再次进行下载,此文件夹能很好的支持这个目标.但需要确 保不要存放任何用户创建的文档或文件到这个文件夹中. 现在,你应当知道应用安装到一台设备时 IOS 系统会创建的文件夹.下一步,你要做的是用 Apple 暴露给我们的 API 得到这些我们正在讨论的非常有用的文件夹的路径(此部分内 容也将在本章节中解释) 1.1. 获得磁盘上最常用文件夹的路径 1.1.1. 问题 你可能希望获得在应用中最常使用文件夹的路径.(例如我们在 12.0 小结中谈论的文 件夹),这样你就可以读取这些文件夹中的内容或在这些文件夹中创建新的内容. 程序开发人员应该使用 IOS SDK 暴露出来的 API 来获取文件夹及/或文 件的路径.换句话说,你不应该假设一个文件夹或文件的路径.而是应该确 保使用合适的 API 来做这件事情,例如,你正在查找 Documents 文件夹 的路径,但你绝不要在你的资源束(bundle)中假设此 Documents 的调用 路径,而是简单的使用合适的 API 来获得路径,另外,如果你想添加或存取 此文件夹中的文件,只需要把文件名添加到此路径的后面. 1.1.2. 解决方案 使用 NSFileManager 类的 URLsForDirectory:inDomains: 实例方法. 1.1.3. 讨论 NSFileManager 类提供了很多与文件及文件夹相关的操作,你只需简单的实例化这个类, 就能在应用中依赖 IOS 正确使用.我不建议使用此类利用 defaultManager 类方法获得 的共享文件管理机制,因为它不是线程安全的.你最好自己创建及管理一个 NSFileManager 实例. NSFileManager 类的 URLsForDirectory:inDomains: 实例方法允许你在 IOS 的文件系统 中搜索指定的目录,特别是在你应用的沙箱中.此方法有两个参数: URLsForDirectory: 此参数是你将要搜索的字典对象.为此参数传递一个 NSSearchPath 类型的目 录字典.我将在稍后详细讨论此参数. inDomains: 此参数指定你在哪儿搜索给定的目录.此参数必须是一 NSSearchDomainMask 的枚举值. 假设你要获得你应用的 Document 文件夹路径,如下,你会发现是如此的简单: NSFileManager *fileManager = [[NSFileManager alloc] init]; NSArray *urls = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; if ([urls count] > 0){ iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 7 版本 1.0 | 2013 年 03 月 17 日 NSURL *documentsFolder = urls[0]; NSLog(@"%@", documentsFolder); } else { NSLog(@"Could not find the Documents folder."); } 正你看到的这样,在创建了一个 NSFileManager 实例后,我们传递 NSDocumentDirectory 来标记我们需要获取的文件夹,传递 NSUserDomainMask 来标记所属域.下面,我们将列 出 NSFileManager 类实例方法 URLsForDirectory:inDomains:的所有你可传递的重要参 数值: URLsForDirectory NSLibraryDirectory 标记应用的 library 文件夹. NSCachesDirectory 标记 caches 文件夹,在之前解释说明过. NSDocumentDirectory 标记 document 文件夹. inDomains NSUserDomainMask 标记对文件夹路径的搜索在当前用户文件夹下进行.在 OS X 中,此文件 夹为~/. 通过此种方法,接下来你可以获得其他如 caches 文件夹的路径,如下所示: NSFileManager *fileManager = [[NSFileManager alloc] init]; NSArray *urls = [fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask]; if ([urls count] > 0){ NSURL *cachesFolder = urls[0]; NSLog(@"%@", cachesFolder); } else { NSLog(@"Could not find the Caches folder."); } 如果你想获得 tmp 文件夹的路径,请像这样使用 C 函数 NSTemporaryDirectory() : NSString *tempDirectory = NSTemporaryDirectory(); NSLog(@"Temp Directory = %@", tempDirectory); 如果你在设备上执行上面的命令,在输出窗口中将显示如下类似的结果: Temp Directory = /private/var/mobile/Applications/19E19C60-8A3A-41BFAFCA-B1AB9746A4CB/tmp/ 1.1.4. 参考 12.0 小节 1.2. 对文件进行读写操作 1.2.1. 问题 你可能希望能保存信息到磁盘上(例如:文本,数据,图像等等). 1.2.2. 方案 Cocoa 类允许你保存信息,如 NSString,UIImage 及 NSData,利用暴露出的实例方法,你 能保存他们的数据到指定的路径. iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 8 版本 1.0 | 2013 年 03 月 17 日 1.2.3. 讨论 为了能够存储文本到磁盘,需要确保文本信息保存在一个 NSString 实例对象(或此类 的不可变版本),然后你可以使用此类的 writeToFile:atomically:encoding:error:实例方法. 此方法使用字符串指向的目标存储路径.此处列出不同的参数: writeToFile 字符串类型的文件存储路径. atomically 一个 Boolean 值,如果设置为 YES,则文件首先会被存储到一个临时空间,然 后此临时文件才会被移到你所设定的目标路径.这样可以确保文件内容被存储 到目标路径之前会首先被存储到磁盘某处,此时,即使 IOS 系统在文件被移到 最终的目标路径前崩溃,你的数据内容在操作系统稍后恢复时仍将得到保存. ,推荐设置此值为 YES,以确保你在保存数据时不因应用运行异常情况导致数 据丢失. encoding 要保存到目标路径的文本字符编码.程序开发人员通常使用 UTF8 编码,则使 用 NSUTF8StringEncoding 常量值. error 当保存操作失败时返回一个 NSError 对象指针,通过此对象,你可以获得保存 过程中发生的错误信息.你也可以为此参数传入一个 nil 指针以表示你对保存过 程中发生的错误信息不感兴趣.最后,记住此函数返回一个 Boolean 值,通过 此值你可以很简单的获知是否有错误发生. 例如,你的应用有一些文本信息需要临时保存,且你不想让 IOS 系统来备份这些内 容,则你可以如下操作: NSString *someText = @"Random string that won't be backed up."; NSString *destinationPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"MyFile.txt"]; NSError *error = nil; BOOL succeeded = [someText writeToFile:destinationPath atomically:YES encoding:NSUTF8StringEncoding error:&error]; if (succeeded) { NSLog(@"Successfully stored the file at: %@", destinationPath); } else { NSLog(@"Failed to store the file. Error = %@", error); } 当然,在完成上面的操作后,为了确保正确,你可以使用 NSString 类的实例方法 stringWithContentsOfFile:encoding:error 尝试从保存路径中读回一些字符串到内存.此 方法将返回一个代表指定文件内容的自动释放 string 对象.如果你确切地想从文件的 内容实例化一个 NSString 对象,则只需简单的使用 NSString 类的实例方法 initWithContentOfFile:encoding:error:,如下: - (BOOL) writeText:(NSString *)paramText toPath:(NSString *)paramPath{ return [paramText writeToFile:paramPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } - (NSString *) readTextFromPath:(NSString *)paramPath{ return [[NSString alloc] initWithContentsOfFile:paramPath encoding:NSUTF8StringEncoding error:nil]; iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 9 版本 1.0 | 2013 年 03 月 17 日 } - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"MyFile.txt"]; if ([self writeText:@"Hello, World!" toPath:filePath]){ NSString *readText = [self readTextFromPath:filePath]; if ([readText length] > 0){ NSLog(@"Text read from disk = %@", readText); } else { NSLog(@"Failed to read the text from disk."); } } else { NSLog(@"Failed to write the file."); } self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; } 上面我们编写了两个方便使用的方法,允许我们对指定位置的文件进行文本读写操 作.在我们的应用代理中,我们先写一些文本到 temp 文件夹下,然后再从这个位置 读相同的文本到内存中,以验证我们编写的方法工作正常. 如果你想对由 NSURL(或者是此对象的可变版本)实例封装的 URLs 指向的文件进 行读写,你可以使用 writeToURL:atomically:encoding:error:实例方法代替. NSURL 实例能够指向本地或远程资源(文件,目录等等).例如,一个 NSURL 实例能很容易的指向 URL 为 www.apple.com 的网站如同指向你应用中 Documents 文件夹下的一个本地文件.此类很容易给你提供通过 URLs 进行存 取的功能,而不管 URL 的类型是什么. 基础库中其他类有与 NSString 类似的方法.以 NSArray 为例.你可以通过 NSArray 的 实例方法 writeToFile:atomically:保存数组的内容到磁盘.为了从磁盘上读取一个 数 组 的 内 容 到 内 存 , 你可以简单的分配一个数组实例 , 然 后 通 过 initWithContentsOfFile:初始化它.如下是这写内容的一个例子: NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"MyFile.txt"]; NSArray *arrayOfNames = @[@"Steve", @"John", @"Edward"]; if ([arrayOfNames writeToFile:filePath atomically:YES]){ NSArray *readArray = [[NSArray alloc] initWithContentsOfFile:filePath]; if ([readArray count] == [arrayOfNames count]){ NSLog(@"Read the array back from disk just fine."); } else { NSLog(@"Failed to read the array back from disk."); } } else { NSLog(@"Failed to save the array to disk."); } iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 10 版本 1.0 | 2013 年 03 月 17 日 NSArray 类的实例方法 writeToFile:atomiclly 只能保存包含如下对象类型的数组: NSString NSDictionary NSArray NSData NSNumber NSDate 如果你试图在数组中插入其他的对象,则数据将无法被保存到磁盘,因为此方法首先会检测数组中的对象是否是上面列出 的类型.原因很简单,因为 Objective-C 运行时将不,换句话说,不知道如何保存你的数据到磁盘上.例如,假设你创建 了一个名叫 Person 的类,并有 first name 及 last name 两个属性,然后实例化一个对象并加入到数组中.但数组怎么把你 的 person 对象保存到磁盘中?很明显不能,因为数组不知道需要保存什么到磁盘.此问题被叫做编组(marshalling), IOS 系统只是对上面列出的数据类型进行了处理. 字典具有和数组类似的进行数据写磁盘及读数据回字典对象的方式.方法名称也完全 相同,且数组的保存规则同样适用于字典,如下所示: NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"MyFile.txt"]; NSDictionary *dict = @{ @"first name" : @"Steven", @"middle name" : @"Paul", @"last name" : @"Jobs", }; if ([dict writeToFile:filePath atomically:YES]){ NSDictionary *readDictionary = [[NSDictionary alloc] initWithContentsOfFile:filePath]; /* Now compare the dictionaries and see if the one we read from disk is the same as the one we saved to disk */ if ([readDictionary isEqualToDictionary:dict]){ NSLog(@"The file we read is the same one as the one we saved."); } else { NSLog(@"Failed to read the dictionary from disk."); } } else { NSLog(@"Failed to write the dictionary to disk."); } 如同你所看到的,上面的例子把一个字典写到磁盘,然后再从相同位置读取出来. 在读取数据后,我们把保存到磁盘的字典与原字典做对比,以确认它们包含相同的 数据. 直到现在,我们都是使用像 NSString 及 N'SArray 这些高层类来保存我们的数据到磁 盘.现在,如果我们想保存原始的字节数组到磁盘呢?同样很简单.假设我们有一 个包含四个无符号字符的数组需要保存到磁盘: unsigned char *bytes = {0x22, 0xA0, 0x41, 0x10}; 最简单的保存方式是把这个原始的字节数组放到一个高层数据结构,像 NSData 对象 中,然后使用 NSData 的相关方法进行磁盘的读写操作.NSData 的文件保存及导入方法 几乎与 NSArray 及 NSDictionary 相同.如下是一个保存及读取原始数据的示例: NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"MyFile.txt"]; char bytes[4] = {'a', 'b', 'c', 'd'}; NSData *dataFromBytes = [[NSData alloc] initWithBytes:bytes length:sizeof(bytes)]; iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 11 版本 1.0 | 2013 年 03 月 17 日 if ([dataFromBytes writeToFile:filePath atomically:YES]){ NSData *readData = [[NSData alloc] initWithContentsOfFile:filePath]; if ([readData isEqualToData:dataFromBytes]){ NSLog(@"The data read is the same data as was written to disk."); } else { NSLog(@"Failed to read the data from disk."); } } else { NSLog(@"Failed to save the data to disk."); } 1.2.4. 参考 12.0 小节 1.3. 在磁盘中创建一个文件夹 1.3.1. 问题 你可能希望在磁盘上创建文件夹并把你的应用的文件保存在里面. 1.3.2. 方案 使用类 NSFileManager 的实例方法 createDirectoryAtPath:withIntermediateDirectories:attributes:error:,如下所示: NSFileManager *fileManager = [[NSFileManager alloc] init]; NSString *tempDir = NSTemporaryDirectory(); NSString *imagesDir = [tempDir stringByAppendingPathComponent:@"images"]; NSError *error = nil; if ([fileManager createDirectoryAtPath:imagesDir withIntermediateDirectories:YES attributes:nil error:&error]){ NSLog(@"Successfully created the directory."); } else { NSLog(@"Failed to create the directory. Error = %@", error); } 1.3.3. 讨论 NSFileManager 暴露出的 API 很容易使用,所以也就不奇怪你能用几行代码在磁盘上 创建文件夹.第一次见到 createDirectoryAtPath:withIntermediateDirectories:attributes:error:会让人还害怕,实际并 没有这么糟糕.我将解释你可以传递的参数值: createDirectoryAtPath 要被创建文件夹的路径. withIntermediateDirectorys 一个 Boolean 值,如果设置为 YES,则在创建最深层文件前,将会创建所有的父 文件夹.例如,如果你想在你应用 tmp 文件夹下名叫 data 的文件夹中创建一个 名叫 images 的文件夹,但是 data 文件夹还不存在,则此时你可以很容易的通过 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 12 版本 1.0 | 2013 年 03 月 17 日 设置 withIntermediateDirectorys 为 YES 来要求系统为你创建 tmp/data/images/文件 夹. attributes 一个传递给系统可以影响文件夹将如何创建的属性字典.此处我们不使用这些参 数,是为了保持示例的简单,但你可以改变这些属性,如文件夹修改日期及时 间,创建日期及时间及其他其他你所希望修改的属性. error 此参数接受一个 NSObject 类型的错误对象,在创建文件时发生的任何错误将 被此对象捕获(populated).通常为此参数传递一个错误对象是一个好的主意, 因为在方法返回失败(返回 NO)时,你可以获取错误信息并判断发生了什么. 1.3.4. 参考 12.1 小节 1.4. 枚举文件和文件夹 1.4.1. 问题 你可能希望能枚举一个文件夹中的所有子文件夹,也希望能枚举一个文件夹下的文件 清单.枚举的意思是你可以很简单的遍历一个文件夹下的所有文件夹及/或文件 1.4.2. 方案 使用如下所示的类 NSFileManager 实例方法 contentsOfDirectoryAtPath:error:,我们枚举 了应用的资源束文件夹下所有的文件,文件夹及符号链接. NSFileManager *fileManager = [[NSFileManager alloc] init]; NSString *bundleDir = [[NSBundle mainBundle] bundlePath]; NSError *error = nil; NSArray *bundleContents = [fileManager contentsOfDirectoryAtPath:bundleDir error:&error]; if ([bundleContents count] > 0 && error == nil){ NSLog(@"Contents of the app bundle = %@", bundleContents); } else if ([bundleContents count] == 0 && error == nil){ NSLog(@"Call the police! The app bundle is empty."); } else { NSLog(@"An error happened = %@", error); } 1.4.3. 讨论 在一些 IOS 应用中,你可能会需要枚举一个文件夹下的内容.我给你举一个例子,此 需求在一些情况马上会变得有点模糊.假设用户要求你从 internet 上下载 10 张图片并 缓存在你的应用里.你这样做了并假设保存这些图像到你手工创建的 tmp/images/文 件夹中.接下来,用户关闭应用并在此启动应用,在应用的 UI 中,你希望已经被下 载的文件能在一个列表中显示出来.你怎么达到这个目的?实际上,此非常简单.你 所需要做的是使用 NSFileManager 类来遍历上述文件夹中的内容.如同你在本节的方 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 13 版本 1.0 | 2013 年 03 月 17 日 案中所看到,NSFileManager 类的实例方法 contentsOfDirectoryAtPath:error:返回一个保 存有目标文件夹下指向文件,文件夹,符号链接的 NSString 对象数组.但是,很难区 分 NSString 对象指向的是文件夹,文件等等.为了从文件管理器对象获得更细粒度的 文件信息,调用 contentsOfDirectoryAtURL:includingPropertiesForKeys:opinions:error:.下 面让我们详细的说明那你可以传递给此方法的各参数: contentsOfDirectoryAtURL 准备遍历的文件夹路径.此路径需要传递为一个 NSURL 实例.如果你不知道如 何创建这个 NSURL 实例,不要担心,我们将在后面讨论. includingPropertiesForKeys 传递一个你希望 IOS 系统为在目标文件夹下遍历到的文件,文件夹或其他项目返 回的属性数组值.例如,你可以指定让项目的创建时间返回到结果数组中,此时 指向遍历到项目的 URL 实例的部分属性值将返回(在框架返回的 NSURL 对象中). 下面是可传递给这个数组 最重要参数的列表: NSURLIsDirectoryKey 稍后允许你判断遍历到的 URL 所指对象是否是目录. NSURLIsReadableKey 稍后允许你判断遍历到的 URL 所指项目对你的应用是否可读. NSURLCreateionDateKey 返回遍历到项目的创建日期. NSURLContentAccessDateKey 返回遍历到项目内容的最后存取日期. NSURLContentModificationDateKey 如同参数名所表示的,此参数允许你判断遍历返回的 URL 所指项目的最后 修改时间. options 此参数只允许 0 或 NSDirectoryEnumerationSkipHiddenFiles 值传入.如果传入的 后面这个参数,如同参数名称所示,所有文件在枚举时将被忽略. error 一个对象的引用,当方法执行失败时,错误信息将存到此对象中.尽可能的传入 一个错误对象通常是个好主意.即使方法执行失败时,你也可以通过失败信息获 得更多的控制权. 现在在对项目如何枚举时,我们有了更多的控制选择,让我们枚举.app 文件夹下的所 有项目并打印出它们的创建,最后修改,最后存取日期.我们也将打印出所枚举项目 是否隐藏与否,是否有存取与否.最后一件事情我们将打印枚举出项目是否是目录, 让我们开始吧: - (NSArray *) contentsOfAppBundle{ NSFileManager *manager = [[NSFileManager alloc] init]; NSURL *bundleDir = [[NSBundle mainBundle] bundleURL]; NSArray *propertiesToGet = @[ NSURLIsDirectoryKey, NSURLIsReadableKey, NSURLCreationDateKey, NSURLContentAccessDateKey, NSURLContentModificationDateKey ]; iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 14 版本 1.0 | 2013 年 03 月 17 日 NSError *error = nil; NSArray *result = [manager contentsOfDirectoryAtURL:bundleDir includingPropertiesForKeys:propertiesToGet options:0 error:&error]; if (error != nil){ NSLog(@"An error happened = %@", error); } return result; } - (NSString *) stringValueOfBOOLProperty:(NSString *)paramProperty ofURL:(NSURL *)paramURL{ NSNumber *boolValue = nil; NSError *error = nil; [paramURL getResourceValue:&boolValue forKey:paramProperty error:&error]; if (error != nil){ NSLog(@"Failed to get property of URL. Error = %@", error); } return [boolValue isEqualToNumber:@YES] ? @"Yes" : @"No"; } - (NSString *) isURLDirectory:(NSURL *)paramURL{ return [self stringValueOfBOOLProperty:NSURLIsDirectoryKey ofURL:paramURL]; } - (NSString *) isURLReadable:(NSURL *)paramURL{ return [self stringValueOfBOOLProperty:NSURLIsReadableKey ofURL:paramURL]; } - (NSDate *) dateOfType:(NSString *)paramType inURL:(NSURL *)paramURL{ NSDate *result = nil; NSError *error = nil; [paramURL getResourceValue:&result forKey:paramType error:&error]; if (error != nil){ NSLog(@"Failed to get property of URL. Error = %@", error); } return result; } - (void) printURLPropertiesToConsole:(NSURL *)paramURL{ NSLog(@"Item name = %@", [paramURL lastPathComponent]); NSLog(@"Is a Directory? %@", [self isURLDirectory:paramURL]); NSLog(@"Is Readable? %@", [self isURLReadable:paramURL]); NSLog(@"Creation Date = %@", [self dateOfType:NSURLCreationDateKey inURL:paramURL]); NSLog(@"Access Date = %@", [self dateOfType:NSURLContentAccessDateKey inURL:paramURL]); NSLog(@"Modification Date = %@", [self dateOfType:NSURLContentModificationDateKey inURL:paramURL]); iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 15 版本 1.0 | 2013 年 03 月 17 日 NSLog(@"-----------------------------------"); } - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSArray *itemsInAppBundle = [self contentsOfAppBundle]; for (NSURL *item in itemsInAppBundle){ [self printURLPropertiesToConsole:item]; } self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; } 程序的输出将于如下所示类似: Item name = en.lproj Is a Directory? Yes Is Readable? Yes Creation Date = 2012-08-11 20:07:49 +0000 Access Date = 2012-08-12 12:31:33 +0000 Modification Date = 2012-08-11 20:07:49 +0000 ----------------------------------- Item name = Enumerating Files and Folders Is a Directory? No Is Readable? Yes Creation Date = 2012-08-12 12:31:30 +0000 Access Date = 2012-08-12 12:31:33 +0000 Modification Date = 2012-08-12 12:31:30 +0000 ----------------------------------- Item name = Info.plist Is a Directory? No Is Readable? Yes Creation Date = 2012-08-11 20:07:48 +0000 Access Date = 2012-08-12 12:31:33 +0000 Modification Date = 2012-08-11 20:07:48 +0000 ----------------------------------- Item name = PkgInfo Is a Directory? No Is Readable? Yes Creation Date = 2012-08-11 20:07:48 +0000 Access Date = 2012-08-11 20:07:48 +0000 Modification Date = 2012-08-11 20:07:48 +0000 ----------------------------------- 此应用最需要注意的事情是我们通过对从文件管理器的每个键值查询结果使用了NSURL类的实例方法 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 16 版本 1.0 | 2013 年 03 月 17 日 getResourceValue:forKey:error:来获得所需值,如创建及修改日期.我们通过传递需求到文件管理器, 让文件管理器为我们取所需信息.然后,当我们得到URL时,我们通过前面的方法获得URL指向项目的不同 属性. 接下来让我们看一下这个应用的不同部分.我将简单的解释我们所写的这些方法: contentsOfAppBundel 此方法将遍历.app文件夹下的所有项目(文件,文件夹,符号链接等等),用一 个数组返回结果.数组中的所有项目都将是一个NSURL类型的对象,且包含此 对象的创建日期,最后修改日期及其他我们在前面讨论过的属性. stringValueOfBOOLProperty:ofURL 此方法将获得URL对象的字符串布尔值(Yes 或 No)属性.例如:URL是否是 一个目录的信息是存储为一个二进制的布尔值,因此,如果想要打印一个布尔值 到控制台,我们需要先把它转化为字符串.对每一个URL,我们将查询两个项 目,将返回包含NSURLIsDirectoryKey及NSURLIsReadableKey布尔值的 NSNumber实例.所以代替写两次代码,此方法将为我们进行NSNumber转换到字 符串Yes或No. isURLDirectory: 传入一个URL检测是否是一个目录.此方法内部使用 stringValueOfBOOLProperty:ofURL,且传入NSURLIsDirectroyKey给它. isURLReadable: 判断对给定的URL,你的应用是否有存取权限.此方法内部同样使用 stringValueOfBOOLProperty:ofURL,且传入NSURLIsDirectroyKey给它. dateOfType:inURL: 由于我们将对每个URL检测三次的属性类型是NSDate类型,我们简单的把相关代 码封装在此方法中,这些代码对URL将传入键值并返回与键值相应的日期. 好,差不多就这样,真的,现在你已经知道如何枚举文件夹并返回文件夹下的所有项目.你 甚至知道如何获得不同项目的不同属性. 1.4.4. 参考 12.1 小节, 12.2 小节 1.5. 删除文件和文件夹 1.5.1. 问题 你曾经在磁盘上创建了一些文件及/或文件夹且不在需要他们它们,则你可能想要删 除它们。 1.5.2. 方案 使用 NSFileManager 类的实例方法 removeItemAtPath:error:或 removeItemAtURL.前一 个方法传递一个字符串类型的路径,后一个传递一个 URL 指向的路径。 1.5.3. 讨论 删除文件及文件夹可能是使用文件管理器能执行的最简单的操作之一。在 IOS 系统 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 17 版本 1.0 | 2013 年 03 月 17 日 中,你首先需要关注你的文件及文件夹存储在什么地方,一旦你进行了存储,在你不 再需要这些文件的时候,需要删除这些文件及文件夹。例如,让我们在 tmp/text 文件 夹中创建 5 个文本文件且随后删除他们。以此同时,我们能在执行删除操作前及后枚 举文件夹中的内容以确认删除操作工作正常。另外,如同你所知,tmp/文件夹在你的 应用安装会就存在,但 tmp/text 不存在,因此首先你需要创建它。在我们删除了文件 后,也需要将这个文件夹删除。 /* Creates a folder at a given path */ - (void) createFolder:(NSString *)paramPath{ NSError *error = nil; if ([self.fileManager createDirectoryAtPath:paramPath withIntermediateDirectories:YES attributes:nil error:&error] == NO){ NSLog(@"Failed to create folder %@. Error = %@", paramPath, error); } } /* Creates 5 .txt files in the given folder, named 1.txt, 2.txt, etc */ - (void) createFilesInFolder:(NSString *)paramPath{ /* Create 10 files */ for (NSUInteger counter = 0; counter < 5; counter++){ NSString *fileName = [NSString stringWithFormat:@"%lu.txt", (unsigned long)counter+1]; NSString *path = [paramPath stringByAppendingPathComponent:fileName]; NSString *fileContents = [NSString stringWithFormat:@"Some text"]; NSError *error = nil; if ([fileContents writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&error] == NO){ NSLog(@"Failed to save file to %@. Error = %@", path, error); } } } /* Enumerates all files/folders at a given path */ - (void) enumerateFilesInFolder:(NSString *)paramPath{ NSError *error = nil; NSArray *contents = [self.fileManager contentsOfDirectoryAtPath:paramPath error:&error]; if ([contents count] > 0 && error == nil){ NSLog(@"Contents of path %@ = \n%@", paramPath, contents); } else if ([contents count] == 0 && error == nil){ NSLog(@"Contents of path %@ is empty!", paramPath); } else { NSLog(@"Failed to enumerate path %@. Error = %@", paramPath, error); } } /* Deletes all files/folders in a given path */ iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 18 版本 1.0 | 2013 年 03 月 17 日 - (void) deleteFilesInFolder:(NSString *)paramPath{ NSError *error = nil; NSArray *contents = [self.fileManager contentsOfDirectoryAtPath:paramPath error:&error]; if (error == nil){ error = nil; for (NSString *fileName in contents){ /* We have the filename, to delete it, we have to have the full path */ NSString *filePath = [paramPath stringByAppendingPathComponent:fileName]; if ([self.fileManager removeItemAtPath:filePath error:&error] == NO){ NSLog(@"Failed to remove item at path %@. Error = %@", fileName, error); } } } else { NSLog(@"Failed to enumerate path %@. Error = %@", paramPath, error); } } /* Deletes a folder with a given path */ - (void) deleteFolder:(NSString *)paramPath{ NSError *error = nil; if ([self.fileManager removeItemAtPath:paramPath error:&error] == NO){ NSLog(@"Failed to remove path %@. Error = %@", paramPath, error); } } - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.fileManager = [[NSFileManager alloc] init]; NSString *txtFolder = [NSTemporaryDirectory() stringByAppendingPathComponent:@"txt"]; [self createFolder:txtFolder]; [self createFilesInFolder:txtFolder]; [self enumerateFilesInFolder:txtFolder]; [self deleteFilesInFolder:txtFolder]; [self enumerateFilesInFolder:txtFolder]; [self deleteFolder:txtFolder]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; } 记住 fileManager 的属性,即我们在应用的代理中使用的各种方法,实际是应用代理 的一个属性,定义在应用的代理头文件中: #import @interface AppDelegate : UIResponder @property (nonatomic, strong) UIWindow *window; @property (nonatomic, strong) NSFileManager *fileManager; @end iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 19 版本 1.0 | 2013 年 03 月 17 日 这个例子融合了你在本章节所学的许多知识,从文件夹的枚举到文件创建,再到文件 的删除,都在这个例子里面。就像你在应用的启动点所看到的,我们执行了 6 个主要 任务,所有这些任务都有关联的方法需要关注: 1. 创建 tmp/txt 文件夹。我们知道 IOS 系统会为每个应用创建 tmp 文件夹,但是 在应用安装到设备时,IOS 系统不会创建 txt 文件夹。 2. 在 tmp/txt 文件夹中创建 5 个文本文件. 3. 枚举 tmp/txt 文件夹中的所有文件,目的是为了证明我们成功进行在文件夹中创 建了文件. 4. 删除已经创建的文件以证明本小结的要点. 5. 再次遍历 tmp/txt 文件夹下的文件以证明删除机制工作正常. 6. 删除 tmp/txt 文件夹,因为我们不再需要它.再次强调,注意你在磁盘上创建的文 件夹及文件.磁盘空间不会自动增加,所以,如果你不再需要那你所创建的文件及 文件夹,请删除它们. 现在你不止知道如何创建文件及文件夹,还知道在不需要它们的时候如何删除它们. 1.5.4. 参考 12.2 小节 1.6. 磁盘中文件的安全处理 1.6.1. 问题 当 IOS 设备被锁定时,你可能希望你正在使用的磁盘文件也被锁定或被加密,让攻击者 不能访问. 1.6.2. 方案 在你使用 NSFileManager 的实例方法 createFileAtPath:contents:attributes 创建文件或使 用 createDirectroyAtPath:withIntermediateDirectories:attributes:error 创建文件夹时,在属 性字典中传入 NSFileProtectionKey 的如下值之一: NSFileProtectionCompeteUnlessOpen 文磁盘件将被加密且只有设备解锁后你的应用才能访问.用户可以再次锁定设备, 但只要你处理过文件,则可以继续对文件进行读写. NSFileProtectionComplete 磁盘文件被加密且只有用户在解锁设备情况下才能存取.你在设备未锁定状态处理 一个文件,当设备再次锁定后,你将不能对文件进行读写. NSFileProtectionCompleteUntilFirstUserAuthentication 磁盘文件被加密,但在用户解锁设备一次后,即可对文件进行读写.从这次设备解锁 后之后,都可以对文件进行读写,即使用户再次锁定设备,也不会影响你及文件的句 柄. IOS 使用用户为设备设定的密码作为敏感数据加密操作的密钥.如果你在磁盘上存储铭 感文件或文件夹,你可以使用 NSFileManager 的这个机制,以确保被安全的存储在磁盘 上. iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 20 版本 1.0 | 2013 年 03 月 17 日 1.6.3. 讨论 如果你注意到,我在上面列表的 NSFileProtectionCompleteUntilFirstUserAuthentication 项中谈论到了文件句柄.文件句柄是什么意思?直到现在,我们都是对磁盘文件进行完整 的数据读写.这很好,但有时候你可能希望打开一个文件,能持续对它进行读写,并为它添 加数据.为达到这个目的,IOS 的 SDK 为程序员提供了 NSFileHandle 类,此类是一个对 文件的高层次抽象,它允许你对文件进行几乎所有的操作. 让我们看一个简单的例子.假设我们写一些文本到 tmp/file.txt 文件中,如下的例子将体 现是这很简单: - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSFileManager *fileManager = [[NSFileManager alloc] init]; NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"file.txt"]; BOOL fileIsCreated = NO; if ([fileManager fileExistsAtPath:filePath] == NO){ fileIsCreated = [fileManager createFileAtPath:filePath contents:nil attributes:nil]; } if (fileIsCreated == YES){ en the file handle */ FileHandle *fileHandle = [NSFileHandle ForUpdatingAtPath:filePath]; if (fileHandle != nil){ NSString *stringToWrite = @"Hello, World!"; /* Write the data */ [fileHandle writeData: [stringToWrite dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"Wrote to the file."); } else { NSLog(@"Failed to create the file handle."); } /* Close the file handle */ [fileHandle closeFile]; /* Delete the file now that we no longer need it */ ileManager removeItemAtPath:filePath error:nil]; } else { NSLog(@"Failed to create the file on disk."); } self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; } 如同你所看到的,我们使用 NSFileManage 的协助来创建不存在的文件且在使用完后删 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 21 版本 1.0 | 2013 年 03 月 17 日 除它.在文件创建后,我们能使用 NSFileManage 类的方法 fileHandleForUpdatinngAtPath 来打开文件并对他进行读写操作.下面是 NSFileManage 类的打开文件的重要方法,可以 达到不同的目的: fileHandleForUpdatingAtPath: 打开文件并获得读写权限,此将把文件指针放在文件的起始处. fileHandleForReadingAtPath: 打开文件并只具有读取权限. fileHandleForWritingAtPath: 打开文件并只具有写权限. 上述所有方法在目标路径上的文件不存在时,都将会创建文件. 当文件打开后,你可以使用 NSFileHandle 类的如下实例方法来进行文件的读写,当然,依 赖于你用什么方法打开文件: readDataOfLength: 返回一个 NSData 对象,包含从文件读取到的 n 字节. writeData: 把指定的 NSData 对象写到文件. 你总可以通过 NSFileHandle 的实例方法 availableData 的返回值获知有多少字节的数 据已被写到了文件中. 现在我们知道了文件的基本操作,让我们继续本小结的主题,文件的安全存储. 假设你正在收集应用运行时的用户相关信息,以在将来使用.当下次应用启动时,你会试 图从这个文件读取信息.如果它不存在,你将会再次创建它.如同 12.0 小结所 述,Library/Caches/文件夹是存放此类数据的最好地方,这些应用创建的数据及文件夹中 的内容将不会被 IOS 备份到备份设备如 iCloud 或 iTunes 中.让我们开始这一切吧.在这 个例子中,我们将使用 NSFileProtectionCompleteUnlessOpen 来对我们的文件进行保护, 此文件我们将命名为 userdata.txt.在示例代码中,有两点需要考虑,我们需要确保:  在要运行应用的设备上我们已经设定了密码.  一旦应用在设备上运行,我们锁定设备则文件将再次被保护. 在如下的示例代码中,我将使用第 14 所包含的技术,因此,不要因为感到奇怪而沮丧.实 际上,在示例代码中你唯一会感觉奇怪的是当应用在运行时用户锁定设备,我们等待应 用从活动状态变为非活动状态的地方.实际上这种情况在用户把他的应用切换到后台时 也会发生,但位简单起见,我们将不考虑这种场景.所以,确保应用在设备上运行时,你按下 了锁定键已让应用转换到非活动模式: - (NSString *) cachesDirectory{ NSArray *caches = [self.fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask]; if ([caches count] > 0){ NSURL *result = caches[0]; iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 22 版本 1.0 | 2013 年 03 月 17 日 return [result path]; } else { return nil; } } - (void) createFileIfDoesntExist:(NSString *)paramPath{ NSDictionary *attributes = @{ NSFileProtectionKey : NSFileProtectionComplete }; if ([self.fileManager fileExistsAtPath:paramPath] == NO){ [self.fileManager createFileAtPath:paramPath contents:nil attributes:attributes]; } } - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.fileManager = [[NSFileManager alloc] init]; NSString *filePath = [[self cachesDirectory] stringByAppendingPathComponent:@"file.txt"]; [self createFileIfDoesntExist:filePath]; self.fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; } - (void) writeToFile{ @try { NSLog(@"Attempting to write to the file..."); [self.fileHandle writeData: [@"Hello World" dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"Successfully wrote to the file. Make sure you have a passcode\ set on your device. This method should have failed!"); } @catch (NSException *exception) { NSLog(@"Failed to write to file. Is it locked?"); } @finally { NSLog(@"Finishing our background task..."); [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask]; } } - (void)applicationWillResignActive:(UIApplication *)application{ if (self.fileHandle == nil){ NSLog(@"The file wasn't opened. No point trying to write to it!"); return; iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 23 版本 1.0 | 2013 年 03 月 17 日 } NSLog(@"Scheduling writing to file in 10 seconds..."); self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ self.backgroundTask = UIBackgroundTaskInvalid; }]; [self performSelector:@selector(writeToFile) withObject:nil afterDelay:10.0f]; } 上述实例代码涉及到的一些方法,将在下面做解释: cacheDirectory 返回你应用资源束中 Library/Caches/目录路径. creaeFileIfDoesntExist: 传入一个文件的路径,如果此文件不存在将创建它.文件创建时把将文件属性 NSFileProtectionKey 的值设置为 NSFileProtectionComplete,这样在设备锁定时,文件 也将被锁定,即使我们锁定设备时文件已经被打开. application:didFinishLaunchingWithOptions: 通过前面描述的方法获得文件的句柄. applicationWillResignActive: 延迟 10 秒执行一个写入操作计划.此方法将会在多种情况下被调用,一种情况就是 用户按下了设备的锁定键,在此处我们就假设是这种情况. writeToFile 执行真正的文件写入操作. 如同你在代码中看到的那样,NSFileHandle 类的实例方法 writeData 在写入数据 失败的情况下会抛出一个异常.此类的实例方法 readDataOfLenght 不能从文件 读取文件时将同样会抛出异常. 如果你在有密码的设备上运行这个应用,当按下设备锁定键 10 秒后,你将在控制台看到 如下类似的信息会显示出来: Scheduling writing to file in 10 seconds... Attempting to write to the file... Failed to write to file. Is it locked? Finishing our background task... 1.6.4. 参考 12.5 小节, 12.2 小节 1.7. 将 Object 保存到文件中 1.7.1. 问题 你添加一个新类到你的项目当中且你希望可以保存这个类的一个实例对象到磁盘文件 并在需要时从磁盘文件读回到内存中. iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 24 版本 1.0 | 2013 年 03 月 17 日 1.7.2. 方案 确保你的类遵循 NSCoding 协议且在类中实现了需要是实现的方法.不要担心,在本节 的讨论部分我会带着你学习. 1.7.3. 讨论 在 IOS SDK 中有两个非常方便类来达到这个目的,在程序开发的术语中叫做编组,他们 是: NSKeyedArchiver 一个利用键值来归档或存储对象或对象树的类.对象的每一个值,我们称为属性, 都能使用程序员选定的键值来归档.你将获得一个归档文件,让后你将可以保存你的 数值通过所选定顶的键值,此很像一个字典. NSKeyedUnarchiver 此类进行与归档类相反的操作.它能很简单地给你未归档的字典并要求你读取值到 属性中. 为了让归档及反归档工作正常,你需要确保需要归档及反归档的对象遵循 NSCoding 协 议.让我们以一个简单的 Person 类开始,如下是这个类的头文件: @interface Person : NSObject @property (nonatomic, copy) NSString *firstName; @property (nonatomic, copy) NSString *lastName; @end 如果现在你不写此类的任何实现代码且进行编译,你将会考到编译器抛出的警告,说你 没有遵循NSCoding协议且没有实现需要实现的方法.需要实现的方法如下: - (void)encodeWithCoder:(NSCoder *)aCoder 此方法将给你一个编码器对象.此编码器对象你可以像一个词典那样使用,可以简单的通过你所选择的键值 存储数值进去. - (id)initWithCoder:(NSCoder *)aDecoder; 当你使用NSKeyedUnarchiver对象反归档你的对象时,此方法将被调用.你可以很简单的通过传递进来的 NSCoder对象获取你回的数值 现在,基于上面的信息,我们实现我们的类: #import "Person.h" NSString *const kFirstNameKey = @"FirstNameKey"; NSString *const kLastNameKey = @"LastNameKey"; @implementation Person - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:self.firstName forKey:kFirstNameKey]; [aCoder encodeObject:self.lastName forKey:kLastNameKey]; } - (id)initWithCoder:(NSCoder *)aDecoder{ self = [super init]; if (self != nil){ _firstName = [aDecoder decodeObjectForKey:kFirstNameKey]; _lastName = [aDecoder decodeObjectForKey:kLastNameKey]; iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 25 版本 1.0 | 2013 年 03 月 17 日 } return self; } @end 你可以看到我们使用NSCoder实例的方式与使用字典非常类似,除了用 encodeObject:forKey代替字典的setValue:forKey及用decodeObjectForKey:代替字典的 objectForKey:.总之,与使用字典的方式非常类似. 我们将在这个类中做这些.所以让我们使用前面介绍过的两个类来实现归档及反归档 机制.在我们的计划中,首先我们将实例化一个Person对象,对它进行归档,并从内存中销 毁它,然后从文件再次读回它,最后我们比较反归档获得的数值是否与我们的原始值匹 配.我们将在我们的应用代理中实现这些,因为此处是最适合做这些事情的地方: #import "AppDelegate.h" #import "Person.h" @implementation AppDelegate - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ /* Define the name and the last name we are going to set in the object */ NSString *const kFirstName = @"Steven"; NSString *const kLastName = @"Jobs"; /* Determine where we want to archive the object */ NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"steveJobs.txt"]; /* Instantiate the object */ Person *steveJobs = [[Person alloc] init]; steveJobs.firstName = kFirstName; steveJobs.lastName = kLastName; /* Archive the object to the file */ [NSKeyedArchiver archiveRootObject:steveJobs toFile:filePath]; /* Now unarchive the same class into another object */ Person *cloneOfSteveJobs = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; /* Check if the unarchived object has the same first name and last name as the previously archived object */ if ([cloneOfSteveJobs.firstName isEqualToString:kFirstName] && [cloneOfSteveJobs.lastName isEqualToString:kLastName]){ NSLog(@"Unarchiving worked"); } else { NSLog(@"Could not read the same values back. Oh no!"); } /* We no longer need the temp file, delete it */ NSFileManager *fileManager = [[NSFileManager alloc] init]; [fileManager removeItemAtPath:filePath error:nil]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 26 版本 1.0 | 2013 年 03 月 17 日 return YES; } 所以归档只需简单的使用NSKeyedArchiver类的实例方法archiveRootObject:toFile即可, 此方法传入一个对象及文件,文件用于存储归档内容.非常简单容易.但是反归档呢?此 和归档过程同样简单,你所需做的只是传递一个归档文件路径给NSKeyedUnarchiver类 的实例方法unarchiveObjectWithFile:,其他所有剩余工作将由NSKeyedUnarchiver对象为 你完成. 1.7.4. 参考 12.1 小节 iOS 6 Programming Cookbook DevDiv 热心网友自发组织翻译 DevDiv 热心网友自发组织翻译 27 版本 1.0 | 2013 年 03 月 17 日 点击这里访问:DevDiv.com 移动开发论坛
还剩26页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 5 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

seekmas

贡献于2013-06-21

下载需要 5 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf