Objective-C & Sprite Kit太空历险记 : 8. 数据管理——文件与目录

原文

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

   

L上尉:大家好,我是太空军事学院的图书管理员,当然也是大家的教官;本课的目的就是讨论如何管理文件,这可以我的本行。对于OS X和iOS星系的人们,信息的数据管理有着相同或不同的习惯,在这里,我们将讨论这两个环境下的文件和目录操作,这些内容包括:

  • 获取OS X和iOS常用系统信息
  • NSData与NSMutableData类
  • NSFileManager类
  • 文件操作
  • 目录操作

8.1. 获取OS X和iOS系统信息

L上尉:首先,我们知道,OS X操作系统的核心是基于UNIX系统的,所以,如果你有UNIX或Linux操作系统的使用经验,应该对一些常用的目录不会太陌生,比如如:

  • 当“/”符号在路径中第一位时表示根目录;请注意,在iOS系统中,它表示当前应用“沙盒(sandbox)”的根目录,在iOS系统中,每一个应用都会有自己独立的“沙盒”,一般情况下,应用只能(也只应该)读写自身“沙盒”中的内容,这样做的目的就两个字——安全!。
  • “~”表示当前登录用户的主目录,而“~<用户名>”表示某一用户的主目录。
  • “.”表示当前目录。
  • “..”表示父目录。

接下来,我们就可以讨论如何获取一些常用的系统信息了。本章的示例,我们可以继续在相应的项目中进行测试,如OCDemo、SimpleOSX或SimpleIOS项目。

8.1.1. 获取文稿目录(Documents)

L上尉:对于应用类项目中,对文稿(Documents)或临时目录(tmp)的操作比较多。在讨论NSArray类时,我们已经看到如何获取当前用户的文稿目录路径了,我们再回顾一下相关代码。

// 获取用户文档存放路径
NSArray *paths = NSSearchPathForDirectoriesInDomains(
    NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths objectAtIndex: 0];

首先是NSSearchPathForDirectoriesInDomains()函数,它的功能就是根据参数搜索系统中的目录,并以NSArray对象形式返回搜索结果。其中的参数:

  • 参数一,我们使用了NSDocumentDirectory,即告诉函数,我们需要搜索文稿(Documents)目录。此外,我们可以通过NSSearchPathDirectory枚举值查询各种系统目录的路径。
  • 参数二,指定搜索范围,我们指定的NSUserDomainMask值告诉函数,我需要搜索的范围是当前的登录用户环境下的目录。我们还可以通过NSSearchPathDomainMask枚举值查看相关的定义。
  • 参数三设置路径中特殊字符的显示。如果设置为NO,则显示特殊字符(目录的系统名称),如当前用户的文稿目录就显示为“~/Documents”;如果设置为YES,则目录会显示完整的物理路径,如“/Users/caohuayu/Documents”。

NSSearchPathForDirectoriesInDomains()函数会返回一个NSArray对象,由于前两个参数指定的目录路径搜索的条件,所以,我们有理由相信结果中的第一个成员就是我们所需要的当前用户的“文稿”目录所在的真实路径,当然,获取NSArray对象中的第一个成员,我们还可以使用firstObject属性,如:

// 获取用户文档存放路径
NSArray *paths = NSSearchPathForDirectoriesInDomains(
    NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = paths.firstObject;

得到“文稿”目录的路径以后,我们就可以将需要的文件连接到路径中,如下面的代码。

// 创建完整文件路径
NSString *filename = 
    [docPath stringByAppendingPathComponent:@"FILENAME"];

然后,我们就可以在代码中通过filename对象使用这个文件的路径了。

8.1.2. 获取临时目录

L上尉:除了“文稿”目录,在项目中,我们的临时文件应该存放于系统的临时目录,获取这个目录路径的方法就简单多了,我们只需要使用NSTemporaryDirectory()函数即可,如:

// 获取临时目录路径
NSString *tmpPath = NSTemporaryDirectory();
NSLog(@"%@", tmpPath);

获取临时目录的路径后,我们经常会需要使用一个临时文件,此时,可以一个有意义的文件名;也可以使用一个临时的文件名,如使用GUID来命名文件,这样可以有效地避免文件名的冲突问题,在Objective-C中,我们可以使用类似如下的代码来获取一个GUID。

// 创建临时文件名
NSString *tmpPath = NSTemporaryDirectory();
NSString *guid = 
    [NSProcessInfo processInfo].globallyUniqueString;
NSString *tmpFilename =
     [tmpPath stringByAppendingPathComponent:guid];
NSLog(@"%@", tmpFilename);

8.1.3. 更多系统信息

L上尉:前面,我们介绍的关于获取文稿目录(Documents)和临时目录(tmp)的方法可以同时应用于OS X和iOS操作系统。如果我们需要获取更多的系统和目录信息,下面会给出一个参考资源,大家有时间可以到图书馆来看看。

NSPathUtilities.h头文件,我们用于获取临时目录的函数就声明在这里,此文件已经在Foundation.h文件中自动引用,所以,我们就不需要单独引用了。而在这个文件中,还有一些常用的函数可以帮助我们获取特定的信息。如:

  • NSUserName()函数,获取当前登录的用户名。
  • NSFullUserName()函数,获取当前用户的完整名称。
  • NSHomeDirectory()函数,获取当前用户的主目录,相当于“~”。
  • NSHomeDirectoryForUser(NSString *user)函数,获取指定用户的主目录,相当于“~<用户名>”。

此外,我们用于获取GUID串的操作使用了NSProcessInfo类,下面,我们就来看看这个类还有哪些常用成员。

processInfo类方法,用于获取当前进程的实例。如:

NSProcessInfo *pi = [NSProcessInfo processInfo];

获取当前进程的实例以后,我们就可以使用如下属性获取一些系统信息。

environment属性,返回一个NSDictionary对象,包括了操作系统中环境变量的信息。下面的代码可以列出所有的环境变量。

NSProcessInfo *pi = [NSProcessInfo processInfo];
NSDictionary *env = pi.environment;
[env enumerateKeysAndObjectsUsingBlock:
    ^(id key, id value, BOOL *stop){
        NSLog(@"%@ = %@", key, value);
    }];

如果我们需要获取某一个环境变量的值,如PATH,可以使用如下代码。

NSProcessInfo *pi = [NSProcessInfo processInfo];
NSDictionary *env = pi.environment;
NSString *pathVar = [env objectForKey:@"PATH"];
NSLog(@"%@", pathVar);

hostName属性返回本地主机的名称,如:

NSProcessInfo *pi = [NSProcessInfo processInfo];
NSLog(@"%@", pi.hostName);

operatingSystemVersionString属性返回当前操作系统版本的描述文本,如:

NSProcessInfo *pi = [NSProcessInfo processInfo];
NSLog(@"%@", pi.operatingSystemVersionString);

8.2. NSData与NSMutableData类

L上尉:在进行数据的交换时,我们经常使用NSData类和其可变版本NSMutableData类作为缓存,在对各种对象、文件及归档的操作中,都有着非常重要的作用。在讨论文件操作之前,我们先来大概了解一下这两个类。

NSData对象的应用相对简单一些,由于它是不可变的对象类型,所以,它的功能就是简单的数据缓存作用,在使用之前,我们可以简单地声明一下,如下面的代码:

NSData *data;

当然,我们也可以使用各种数据初始化NSData对象,此时,我们可以使用NSData类中定义的名称以data开始的一系列类方法。

相对NSData而言,NSMutableData的功能就要强大的多,它定义为NSData类的子类;应用过程中,我们可以修改NSMutableData对象中的内容,然后,可以保存到文件,也可以复制到其它对象当中。当我们定义一个空的NSMutableData对象作为数据缓冲区时,可以通过以下代码来完成。

NSMutalbeData *buffer = [NSMutableData data];

接下来的文件操作,以及下一章的归档操作中,我们可以看到NSData和NSMutableData的具体应用。

8.3. 使用NSFileManager类

L上尉:使用NSFileManager类方便、快捷地操作文件和目录。通常情况下,我们可以通过类方法defaultManager创建一个文件管理对象,如下面的代码。

NSFileManager *fm = [NSFileManager defaultManager];

L上尉:在NSFileManager对象中,有一些成员是文件和目录操作通用的,我们就来了解一下。对于操作的文件,我们假设为“文稿”中的myArrayObject,而目录则是“文稿”中的myDir。我们可以通过下面的代码初始化这两个文件的路径:

// 文稿路径
NSArray *paths = NSSearchPathForDirectoriesInDomains(
    NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = paths.firstObject;
// 测试文件
NSString *filename = 
    [docPath stringByAppendingPathComponent:@"myArrayObject"];
// 测试目录
NSString *dir = 
    [docPath stringByAppendingPathComponent:@"myDir"];
// 文件管理对象
NSFileManager *fm = [NSFileManager defaultManager];
// 其它测试代码

L上尉:请注意,这些代码是以下测试的基础,对于下面的操作,这些代码是必须的,当然,我们也只需要输入一遍就可以了。

8.3.1. 文件或目录是否存在

判断一个文件或目录是否存在,我们可以使用fileExistsAtPath:方法或fileExistsAtPath::方法,如下面的代码。

// 判断文件是否存在
NSLog(@"%i", [fm fileExistsAtPath:filename]);
// 判断目录是否存在
BOOL isDir;
[fm fileExistsAtPath:dir isDirectory:&isDir]
NSLog(@"%i", isDir);

8.3.2. 复制文件和目录

L上尉:当我们需要复制文件或目录时,可以使用copyItemAtPath:::方法,它的定义如下:

- (BOOL)copyItemAtPath:(NSString *)srcPath 
    toPath:(NSString *)dstPath 
    error:(NSError * _Nullable *)error

此方法包括三个参数:

  • 参数一为源文件或目录;
  • 参数二为目标文件或目录,请注意,复制操作时,目标文件或目录不能已存在;
  • 参数三包括操作的错误对象,如果不需要自己处理错误,可以使用NULL值。

如果复制成功,则返回YES值,否则返回NO值。下面的代码,我们将文稿中的myArrayObject文件复制为myArrayObject.bak文件。第一次操作时会显示1(YES),再次操作时会显示0(NO),因为操作一次后,目标文件就已经存在了。

// 复制文件
NSString *target = [filename stringByAppendingString:@".bak"];
NSLog(@"%i", [fm copyItemAtPath:filename 
    toPath:target error:NULL]);

我们可以通过Finder查看操作结果,如下图。

如果你想测试一下myArrayObject.bak文件的内容是否和myArrayObject文件一致,可以载入看一下,如下面的代码。

NSString *target = [filename stringByAppendingString:@".bak"];
NSArray *arr = [NSArray arrayWithContentsOfFile:target];
[arr enumerateObjectsUsingBlock:
    ^(id obj, NSUInteger idx,BOOL *stop){
            NSLog(@"%@", obj);
        }];

L上尉:复制目录时,会复制它的完整结构,在大多数情况下,这应该就是我们所需要的结果。

8.3.3. 删除文件和目录

L上尉:删除文件或目录时,我们使用removeItemAtPath::方法,删除操作时,应注意文件或目录的属性和权限,而删除目录时则需要注意,此方法只能删除已存在的目录。操作成员,方法会返回YES值,否则返回NO值。如下面的代码。

NSLog(@"%i", [fm removeItemAtPath:dir error:NULL]);

代码会显示0(NO),因为myDir目录并不存在。接下来,我们可以在“文稿”目录中手工创建这个目录,再测试删除操作,并观察操作的结果。

8.3.4. 移动、重命名文件和目录

L上尉:使用NSFileManager对象的moveItemAtPath:::方法,当目标在当前位置时,我们可以修改文件或目录的名称;如果目标不在当前目录,则文件或目标会移动到新的位置。操作成功时,方法返回YES,否则返回NO。

如下面的代码,我们将myArrayObject文件重命名为myArrayObject.old。

NSString *target = [filename stringByAppendingString:@".old"];
NSLog(@"%i", [fm moveItemAtPath:filename toPath:target error:NULL]);

使用moveItemAtPath:::方法时,应注意,如果目标文件或目录已存在,则操作不会成功。

8.3.5. 文件与目录的属性

L上尉:操作文件和目录时,它们的属性非常重要,决定了我们能够的操作类型以及一些文件和目录信息。在获取文件或目录的属性列表时,我们可以使用attributesOfItemAtPath::方法,此方法将返回文件或目录的属性列表,定义为NSDictionary对象。

获取的文件和目录信息以后,我们可以通过在NSFileManager.h头文件中定义的NSDictionary分类(NSFileAttributes)成员来获取,如:

- (unsigned long long)fileSize;
- (nullable NSDate *)fileModificationDate;
- (nullable NSString *)fileType;
- (NSUInteger)filePosixPermissions;
- (nullable NSString *)fileOwnerAccountName;
- (nullable NSString *)fileGroupOwnerAccountName;
- (NSInteger)fileSystemNumber;
- (NSUInteger)fileSystemFileNumber;
- (BOOL)fileExtensionHidden;
- (OSType)fileHFSCreatorCode;
- (OSType)fileHFSTypeCode;
- (BOOL)fileIsImmutable;
- (BOOL)fileIsAppendOnly;
- (nullable NSDate *)fileCreationDate;
- (nullable NSNumber *)fileOwnerAccountID;
- (nullable NSNumber *)fileGroupOwnerAccountID;

如下面的代码,我们将显示myArrayObject文件的尺寸、创建时间,以及文件类型。

NSDictionary *attr = 
    [fm attributesOfItemAtPath:filename error:NULL];
NSLog(@"%@", attr.fileCreationDate);
NSLog(@"%llu", attr.fileSize);
NSLog(@"%@", attr.fileType);

此外,这些代码同样适用于目录信息的获取,如下面的代码,我们将显示“文稿”目录的修改时间、尺寸和类型。

NSDictionary *attr = 
    [fm attributesOfItemAtPath:docPath error:NULL];
NSLog(@"%@", attr.fileModificationDate);
NSLog(@"%llu", attr.fileSize);
NSLog(@"%@", attr.fileType);

另一个与文件和目录属性相关的方法是setAttributes:::,它的定义如下。

- (BOOL)setAttributes:(
    NSDictionary<NSString *,id> *)attributes
    ofItemAtPath:(NSString *)path
    error:(NSError * _Nullable *)error

其中,参数一是包含了属性列表的NSDictionary对象,其键(Key)应该使用NSFileSize这样的常量来定义,它们也都定义在NSFileManager.h头文件中。参数二是需要设置属性的文件或目录。参数三,如果不需要我们处理错误可以设置为NULL值。

L上尉:虽然可以设置文件和目录的属性,但在一般的应用中,我们并不建议这样做。理论上讲,管理文件和目录是操作系统的工作,因此,我们的应用程序只做必要的操作就可以了。

8.4. 文件操作

L上尉:接下来,我们将讨论如何使用NSFileManager对象对文件进行一些简单的操作,如文件的读写、比较与读取权限检查等。

8.4.1. 读取文件内容

读取文件内容时,我们使用contentsAtPath:方法,可以一次性读取文件的所有内容,并以NSData对象的形式返回。如下面的代码:

NSData *data = [fm contentsAtPath:filename];

如果文件不存在或读取失败,则会返回空对象(nil值)。

8.4.2. 写入文件

L上尉:前面,我们讨论字符串和数据容器类型(如NSArray等)时已经了解到,这些类型的对象都可以直接使用自身定义的方法将内容写到文件中,而在NSFileManager对象中,我们对文件的读写将主要依靠NSData对象。

如下面的代码,我们将一个通过NSData对象进行myArrayObject文件的复制操作。

NSString *target =
    [docPath stringByAppendingPathComponent:@"myArrayObject1"];
NSData *data = [fm contentsAtPath:filename];
NSLog(@"%i", [fm createFileAtPath:target contents:data attributes:nil]);

请注意代码中使用的createFileAtPath:::方法,它的参数包括:

  • 参数一指定需要写入内容的文件(NSString对象)。
  • 参数二指定需要写入的内容,定义为NSData对象。
  • 参数三可以指定文件的属性列表(NSDictionary对象),如果使用系统默认的属性设置,可以将参数指定为nil值。

L上尉:请大家注意,如果指定的文件不存在,则会自动创建它;如果文件已存在,会使用data对象的内容完全重写这个文件。

8.4.3. 比较文件内容

L上尉:如果我们需要比较两个文件的内容是否相同,可以使用contentsEqualAtPath::方法,其中参数一和参数二分别指定需要对比的两个文件路径;如果两个文件的内容相同,则方法返回YES值,否则返回NO值。如下面的代码。

NSString *target =
    [docPath stringByAppendingPathComponent:@"myArrayObject1"];
NSLog(@"%i", [fm contentsEqualAtPath:filename andPath:target]);

8.4.4. 检测文件读写权限

L上尉:对于文件最基本的读写权限的检测,我们可以使用isReadableFileAtPath:方法和isWritableFileAtPath:方法,它的返回结果都定义为BOOL类型。如下面的代码,我们分别检测了myArrayObject文件读写权限。

NSLog(@"%i", [fm isReadableFileAtPath:filename]);
NSLog(@"%i", [fm isWritableFileAtPath:filename]);

8.5. 目录操作

L上尉:当我们需要创建一个目录时,可以使用createDirectoryAtPath::::方法,它包括四个参数:

  • 参数一指定需要创建的目录路径。
  • 参数二是否创建过渡目录,即是否自动创建多级目录。
  • 参数三指定目录的属性列表(NSDictionary对象),使用系统默认设置可以使用nil值。
  • 参数四设置为NSError对象,如果不需要自己处理错误,则可以设置为NULL值。

如下面的代码。

NSString *target = [dir stringByAppendingPathComponent:@"a/b/c"];
NSLog(@"%i", [fm createDirectoryAtPath:target 
    withIntermediateDirectories:YES 
    attributes:nil 
    error:NULL]);

L上尉:请注意,此代码的功能是在“文稿”中创建一个多级目录,即~/Documents/myDir/a/b/c目录。如果我们将第二个参数设置为NO,此操作就会失败,因为myDir目录中并不包含a目录,无法创建其子目录。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值