[iOS翻译]《iOS 7 Programming Cookbook》:iOS文件与文件夹管理(下)

三. 创建文件夹

问题:

你想创建文件夹到磁盘,存储一些文件到里面

 

解决方案:

使NSFileManager类的实例方法createDirectoryAtPath:withIntermediateDirectories:attributes:error:,代码如下:

 1 - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

 2         NSFileManager *fileManager = [[NSFileManager alloc] init];

 3 

 4         NSString *tempDir = NSTemporaryDirectory();

 5         NSString *imagesDir = [tempDir stringByAppendingPathComponent:@"images"];

 6 

 7     NSError *error = nil;

 8     if ([fileManager createDirectoryAtPath:imagesDir

 9                    withIntermediateDirectories:YES

10                                     attributes:nil

11                                          error:&error]){

12             NSLog(@"Successfully created the directory.");

13 

14     } else {

15         NSLog(@"Failed to create the directory. Error = %@", error);

16     }

17 

18     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

19     self.window.backgroundColor = [UIColor whiteColor]; 

20     [self.window makeKeyAndVisible];

21     return YES;

22 }

 

讨论

NSFileManager如此简洁易用,仅用几行就搞定了文件夹创建,下面是方法参数:

  • createDirectoryAtPath
    • 创建文件夹的路径  
  • withIntermediateDirectories
    • BOOL类型。如果设为YES,将会自动补全最终文件夹之前的中间目录  
    • 例如,如果你想在tmp/data目录创建一个images文件夹,但data文件夹并不存在,怎么办?只需要把withIntermediateDirectories参数设为YES,系统就会自动创建目录tmp/data/images/
  • attributes
    • 通常设为nil  
  • error
    • 接受一个指针指向NSError对象  

 

四. 枚举文件/文件夹

问题:

你想在一个文件夹里枚举文件/文件夹列表,这个枚举动作意味着你想找到所有文件/文件夹

 

解决方案:

使用NSFileManager类的实例方法contentsOfDirectoryAtPath:error:。例如我们想要在bundle文件夹下枚举所有文件/文件夹,代码如下:

 1 - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

 2     NSFileManager *fileManager = [[NSFileManager alloc] init];

 3     NSString *bundleDir = [[NSBundle mainBundle] bundlePath];

 4     NSError *error = nil;

 5     NSArray *bundleContents = [fileManager

 6                                    contentsOfDirectoryAtPath:bundleDir

 7                                    error:&error];

 8 

 9     if ([bundleContents count] > 0 && error == nil){

10       NSLog(@"Contents of the app bundle = %@", bundleContents);

11      }

12    else if ([bundleContents count] == 0 && error == nil){

13      NSLog(@"Call the police! The app bundle is empty.");

14      }

15    else {

16      NSLog(@"An error happened = %@", error);

17    }

18 

19   self.window = [[UIWindow alloc]

20                  initWithFrame:[[UIScreen mainScreen] bounds]];

21   self.window.backgroundColor = [UIColor whiteColor];

22    [self.window makeKeyAndVisible]; 

23    return YES;

24 }

 

讨论

在一些APP里,你可能需要枚举一个文件夹的内容,让我们来看一个例子吧。

想象一下,用户从网络下载了10张图片并缓存到APP里,这时你需要存储它们,也就是说,你要手动创建tmp/images/目录。现在用户关闭后又打开了APP,在界面上,你想要在一个table view里展示下载完成列表,该如何实现?

其实很简单,需要做的就是在目录里使用NSFileManager类进行内容枚举。在解决方案部分,你已经使用了NSFileManager类的实例方法contentsOfDirectoryAtPath:error:进行枚举,然而这个方法并不能指出哪个是文件,哪个是文件夹等等。想要从文件管理里获取更多细节,调用方法contentsOfDirectoryAtURL:includingPropertiesForKeys:options:error:,参数如下:

  • contentsOfDirectoryAtURL
    • 想要检索的文件夹路径(NSURL类型)  
  • includingPropertiesForKeys
    • NSArray类型,代表检索目录下个各个条目的信息  
    • NSURLIsDirectoryKey  
      • 是否是一个字典  
    • NSURLIsReadableKey  
      • 是否可读    
    • NSURLCreationDateKey  
      • 创建日期    
    • NSURLContentAccessDateKey  
      •  访问日期    
    • NSURLContentModificationDateKey  
      • 修改日期    
  • options
    • 参数只能为0或NSDirectoryEnumerationSkipsHiddenFiles,后者在枚举时会跳过隐藏内容  
  • error
    • 接受一个指针指向NSError对象  

 

现在我们在XXX.app目录下进行枚举,并打印各条目的名字、是否为字典、是否可读以及创建/最后修改/最后访问的日期,代码如下;

 1 - (NSArray *) contentsOfAppBundle{

 2      NSFileManager *manager = [[NSFileManager alloc] init]; NSURL *bundleDir = [[NSBundle mainBundle] bundleURL];

 3 

 4         NSArray *propertiesToGet = @[

 5                                      NSURLIsDirectoryKey,

 6                                      NSURLIsReadableKey,

 7                                      NSURLCreationDateKey,

 8                                      NSURLContentAccessDateKey,

 9                                      NSURLContentModificationDateKey

10                                      ];

11 

12      NSError *error = nil;

13         NSArray *result = [manager contentsOfDirectoryAtURL:bundleDir

14                                  includingPropertiesForKeys:propertiesToGet

15                                                     options:0

16                                   error:&error];

17 

18     if (error != nil){

19         NSLog(@"An error happened = %@", error);

20     }

21       return result; 

22 }

23         
 1 - (NSString *) stringValueOfBoolProperty:(NSString *)paramProperty ofURL:(NSURL *)paramURL{

 2     NSNumber *boolValue = nil;

 3     NSError *error = nil;

 4     [paramURL getResourceValue:&boolValue

 5                         forKey:paramProperty

 6                          error:&error];

 7 

 8     if (error != nil){

 9         NSLog(@"Failed to get property of URL. Error = %@", error);

10     }

11         return [boolValue isEqualToNumber:@YES] ? @"Yes" : @"No";

12 }        
1 - (NSString *) isURLDirectory:(NSURL *)paramURL{

2     return [self stringValueOfBoolProperty:NSURLIsDirectoryKey ofURL:paramURL];

3 }

4 

5 - (NSString *) isURLReadable:(NSURL *)paramURL{

6     return [self stringValueOfBoolProperty:NSURLIsReadableKey ofURL:paramURL];

7 }
 1 - (NSDate *) dateOfType:(NSString *)paramType inURL:(NSURL *)paramURL{ 

 2     NSDate *result = nil;

 3     NSError *error = nil;

 4     [paramURL getResourceValue:&result

 5                         forKey:paramType

 6                          error:&error];

 7 

 8     if (error != nil){

 9         NSLog(@"Failed to get property of URL. Error = %@", error);

10     }

11     return result; 

12 }
 1 - (void) printURLPropertiesToConsole:(NSURL *)paramURL{ 

 2 

 3     NSLog(@"Item name = %@", [paramURL lastPathComponent]); 

 4 

 5     NSLog(@"Is a Directory? %@", [self isURLDirectory:paramURL]); 

 6 

 7     NSLog(@"Is Readable? %@", [self isURLReadable:paramURL]); 

 8 

 9     NSLog(@"Creation Date = %@",

10           [self dateOfType:NSURLCreationDateKey inURL:paramURL]);

11 

12     NSLog(@"Access Date = %@",

13     [self dateOfType:NSURLContentAccessDateKey inURL:paramURL]);

14 

15     NSLog(@"Modification Date = %@",

16               [self dateOfType:NSURLContentModificationDateKey inURL:paramURL]);

17 

18     NSLog(@"-----------------------------------");

19 }    
 1 - (BOOL) application:(UIApplication *)application

 2       didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

 3     NSArray *itemsInAppBundle = [self contentsOfAppBundle]; 

 4     for (NSURL *item in itemsInAppBundle){

 5             [self printURLPropertiesToConsole:item];

 6         }

 7 

 8     self.window = [[UIWindow alloc]

 9                        initWithFrame:[[UIScreen mainScreen] bounds]];

10         // Override point for customization after application launch.

11     self.window.backgroundColor = [UIColor whiteColor]; 

12     [self.window         makeKeyAndVisible];

13     return YES;

14 }

输出结果类似下列信息:

    Item name = Assets.car

    Is a Directory? No

    Is Readable? Yes

    Creation Date = 2013-06-25 16:12:53 +0000

    Access Date = 2013-06-25 16:12:53 +0000

    Modification Date = 2013-06-25 16:12:53 +0000

    -----------------------------------

    Item name = en.lproj

    Is a Directory? Yes

    Is Readable? Yes

    Creation Date = 2013-06-25 16:12:53 +0000

    Access Date = 2013-06-25 16:15:02 +0000

    Modification Date = 2013-06-25 16:12:53 +0000

    -----------------------------------

    Item name = Enumerating Files and Folders

    Is a Directory? No

    Is Readable? Yes

    Creation Date = 2013-06-25 16:15:01 +0000

    Access Date = 2013-06-25 16:15:04 +0000

    Modification Date = 2013-06-25 16:15:01 +0000

    -----------------------------------

我们来看看这段代码的一些方法:

  • contentsOfAppBundle
    • 这个方法搜索.app文件夹的所有条目(文件、文件夹等等),并返回一个数组  
    • 数组里的所有条目都为NSURL类型,并包含了创建/最后修改日期以及其他之前提到的属性  
  • stringValueOfBoolProperty:ofURL:
    • 返回与BOOL类型相当的NSString(YES或NO),用来判断URL的信息  
  • isURLDirectory:
    • 是否是字典  
  • isURLReadable:
    • 是否可读  
  • dateOfType:inURL:
    • 日期类型  

现在,你已经知道如何在一个文件夹里遍历所有条目,甚至学会了检索各个条目的不同属性。

 

五. 删除文件/文件夹

问题:

你想删除一些文件或文件夹

 

解决方案:

使用NSFileManager类的实例方法removeItemAtPath:error:(接收string)或者removeItemAtURL:error:(接收URL)

 

讨论

删除文件/文件夹应该是使用file manager时最简单的操作了。现在,让我们来创建5个text文件到tmp/text目录,然后进行删除操作,代码如下;

 1 /* Creates a folder at a given path */

 2 - (void) createFolder:(NSString *)paramPath{

 3     NSError *error = nil;

 4     if ([self.fileManager createDirectoryAtPath:paramPath

 5                         withIntermediateDirectories:YES

 6                                          attributes:nil

 7                                               error:&error] == NO){

 8             NSLog(@"Failed to create folder %@. Error = %@",

 9                   paramPath,

10                   error);

11     } 

12 }
 1   /* Creates 5 .txt files in the given folder, named 1.txt, 2.txt, etc */

 2 - (void) createFilesInFolder:(NSString *)paramPath{ 

 3     /* Create 10 files */

 4     for (NSUInteger counter = 0; counter < 5; counter++){

 5         NSString *fileName = [NSString stringWithFormat:@"%lu.txt", (unsigned long)counter+1];

 6         NSString *path = [paramPath stringByAppendingPathComponent:fileName]; 

 7         NSString *fileContents = [NSString stringWithFormat:@"Some text"]; 

 8         NSError *error = nil;

 9         if ([fileContents writeToFile:path

10                            atomically:YES

11                              encoding:NSUTF8StringEncoding

12                                 error:&error] == NO){

13             NSLog(@"Failed to save file to %@. Error = %@", path, error);

14         } 

15     }

16 }    
 1 /* Enumerates all files/folders at a given path */

 2 - (void) enumerateFilesInFolder:(NSString *)paramPath{

 3     NSError *error = nil;

 4     NSArray *contents = [self.fileManager contentsOfDirectoryAtPath:paramPath

 5                                                               error:&error];

 6 

 7     if ([contents count] > 0 && error == nil){

 8     NSLog(@"Contents of path %@ = \n%@", paramPath, contents); 

 9     }

10     else if ([contents count] == 0 && error == nil){

11         NSLog(@"Contents of path %@ is empty!", paramPath);

12     }

13     else {

14         NSLog(@"Failed to enumerate path %@. Error = %@", paramPath, error);

15     } 

16 }         
 1 /* Deletes all files/folders in a given path */

 2 - (void) deleteFilesInFolder:(NSString *)paramPath{

 3     NSError *error = nil;

 4     NSArray *contents = [self.fileManager contentsOfDirectoryAtPath:paramPath error:&error];

 5 

 6     if (error == nil){

 7          error = nil;

 8         for (NSString *fileName in contents){

 9         /* We have the file name, to delete it,

10          we have to have the full path */

11         NSString *filePath = [paramPath

12                       stringByAppendingPathComponent:fileName];

13         if ([self.fileManager removeItemAtPath:filePath error:&error] == NO){

14             NSLog(@"Failed to remove item at path %@. Error = %@", fileName, error);

15             } 

16         }

17     } else {

18         NSLog(@"Failed to enumerate path %@. Error = %@", paramPath, error);

19     } 

20 }                                    
1  /* Deletes a folder with a given path */

2 - (void) deleteFolder:(NSString *)paramPath{

3     NSError *error = nil;

4     if ([self.fileManager removeItemAtPath:paramPath error:&error] == NO){

5             NSLog(@"Failed to remove path %@. Error = %@", paramPath, error);

6         }

7 }
1 #import "AppDelegate.h"

2 

3 @interface AppDelegate ()

4 @property (nonatomic, strong) NSFileManager *fileManager; 

5 @end

6 

7 @implementation AppDelegate

8 

9 <# Rest of your app delegate code goes here #>

示例代码结合了这一章的很多知识,下面来看一看这些方法的步骤:

  • 1.创建了tmp/txt目录。我们知道tmp文件夹在APP安装时自动创建,但txt文件夹则需要手动创建
  • 2.在tmp/txt目录创建5个text文件
  • 3.在tmp/txt目录枚举所有文件
  • 4.在tmp/txt目录删除文件
  • 5.再次在tmp/txt目录枚举剩下的文件
  • 6.在tmp/txt目录删除文件夹

现在你不光学会了如何创建文件/文件夹,还学会了如何在不需要时删除它们。

 

 

六. 存储对象到文件 

问题:

你添加了一些新类到项目里,这时你想把这些对象作为文件存储到磁盘里,当需要时能随时读取

 

解决方案:

确保你的类遵从NSCoding协议,并且实现协议必要方法

 

讨论

iOS SDK里有两个非常方便的类来实现这一目标,在编程世界里称为marshalling,它们是:

  • NSKeyedArchiver
    • 归档  
  • NSKeyedUnarchiver
    • 解档  

为了实现归档/解档工作,需要遵从NSCoding协议,现在来创建一个简单的Person类,头文件如下:

1 #import <Foundation/Foundation.h>

2 

3 @interface Person : NSObject <NSCoding> 

4 

5 @property (nonatomic, copy) NSString *firstName;

6 @property (nonatomic, copy) NSString *lastName; 

7 

8 @end

协议要求必须实现两个方法:

  • - (void)encodeWithCoder:(NSCoder *)aCoder
    • 给你一个coder,使用方法类似字典  
  • - (instancetype)initWithCoder:(NSCoder *)aDecoder
    • 当你试图使用NSKeyedUnarchiver进行解档时,这个方法会被调用  

现在来来到实现文件,代码如下:

 1 #import "Person.h"

 2 

 3 NSString *const kFirstNameKey = @"FirstNameKey";

 4 NSString *const kLastNameKey = @"LastNameKey";

 5 

 6 @implementation Person

 7 

 8 - (void)encodeWithCoder:(NSCoder *)aCoder{

 9     [aCoder encodeObject:self.firstName forKey:kFirstNameKey];  

10     [aCoder encodeObject:self.lastName forKey:kLastNameKey];

11 }

12 

13 - (instancetype)initWithCoder:(NSCoder *)aDecoder{ self = [super init];

14     if (self != nil){

15             _firstName = [aDecoder decodeObjectForKey:kFirstNameKey];

16             _lastName = [aDecoder decodeObjectForKey:kLastNameKey];

17         }

18     return self; 

19 }

20 

21 @end

接着在AppDelegate实现如下方法:

 1 #import "AppDelegate.h"

 2 #import "Person.h"

 3 

 4 @implementation AppDelegate

 5 

 6 - (BOOL) application:(UIApplication *)application

 7       didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

 8         /* Define the name and the last name we are going to set in the object */

 9     NSString *const kFirstName = @"Steven"; 

10     NSString *const kLastName = @"Jobs";

11 

12         /* Determine where we want to archive the object */

13         NSString *filePath = [NSTemporaryDirectory()

14     stringByAppendingPathComponent:@"steveJobs.txt"];

15 

16         /* Instantiate the object */

17         Person *steveJobs = [[Person alloc] init];

18         steveJobs.firstName = kFirstName;

19         steveJobs.lastName = kLastName;

20 

21         /* Archive the object to the file */

22         [NSKeyedArchiver archiveRootObject:steveJobs toFile:filePath];

23 

24         /* Now unarchive the same class into another object */

25         Person *cloneOfSteveJobs =

26         [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

27 

28         /* Check if the unarchived object has the same first name and last name

29          as the previously-archived object */

30         if ([cloneOfSteveJobs.firstName isEqualToString:kFirstName] 

31             && [cloneOfSteveJobs.lastName isEqualToString:kLastName]){ 

32             NSLog(@"Unarchiving worked");

33         } else {

34             NSLog(@"Could not read the same values back. Oh no!");

35         }

36 

37         /* We no longer need the temp file, delete it */

38         NSFileManager *fileManager = [[NSFileManager alloc] init];

39         [fileManager removeItemAtPath:filePath error:nil];

40         self.window = [[UIWindow alloc]

41                        initWithFrame:[[UIScreen mainScreen] bounds]];

42         self.window.backgroundColor = [UIColor whiteColor];

43         [self.window makeKeyAndVisible];

44         return YES; 

45 }                

可见,归档只需要使用NSKeyedArchiver类的类方法archiveRootObject:toFile就能搞定,那如何解档呢?使用另外一个类方法unarchiveObjectWithFile:就行。

 

 

你可能感兴趣的:(programming)