zoukankan      html  css  js  c++  java
  • [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 %@ = 
    %@", 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:就行。

     

     

  • 相关阅读:
    123457123457#0#-----com.threeapp.renzhepaoku01----儿童跑酷游戏(忍者版)
    123456123456#0#-----com.threeapp.xiongMaoPaoPao01----熊猫跑酷01
    123456123456#0#-----com.threeapp.JiQiRenDaZhan01----机器人大战恐龙
    HashMap的工作原理
    Java 虚拟机
    分布式之缓存系统
    分库分表之后,id 主键如何处理?
    如何设计可以动态扩容缩容的分库分表方案?
    为什么要分库分表?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?
    如何实现 MySQL 的读写分离?MySQL 主从复制原理的是啥?如何解决 MySQL 主从同步的延时问题?
  • 原文地址:https://www.cnblogs.com/yangfaxian/p/3871315.html
Copyright © 2011-2022 走看看