Foundation框架 之 NSFileManager 与 copy & mutableCopy

Posted __Sunshine_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Foundation框架 之 NSFileManager 与 copy & mutableCopy相关的知识,希望对你有一定的参考价值。

本文主要介绍以下几部分:

一、NSFileManager

NSFileManager 是用来管理文件系统的,可以进行常见的文件/文件夹操作(拷贝、剪切、创建等)。
NSFileManager使用了单例模式(singleton),可以使用 NSFileManager 的类方法获得那个默认的单例对象:

+ (NSFileManager *)defaultManager;

1、常见操作:

基本操作:

- (BOOL)fileExistsAtPath:(NSString *)path;
//判断在路径 path 的文件或文件夹是否存。存在则返回YES,不存在或者路径不确定则返回NO
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory;
//判断在路径path的文件或文件夹是否存在,并判断是否为文件夹
//形参isDirectory是BOOL指针,可传进一个BOOL变量的地址,如果判断结果path 是文件夹,则该BOOL变量为YES,否则为NO
- (BOOL)isReadableFileAtPath:(NSString *)path;
// 判断文件(文件夹)是否可读
- (BOOL)isWritableFileAtPath:(NSString *)path;
// 判断文件(文件夹)是否可写
- (BOOL)isExecutableFileAtPath:(NSString *)path;
// 判断文件(文件夹)是否可执行
- (BOOL)isDeletableFileAtPath:(NSString *)path;
// 判断文件(文件夹)是否可删除

深入了解:

- (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error NS_AVAILABLE(10_5, 2_0);
// 获取文件(文件夹)的属性,返回的是 NSDictionary,其中的键值对存放了文件(文件夹)的属性
- (NSArray *)subpathsAtPath:(NSString *)path;
// 获取指定路径下所有的子路径,返回一个数组,每个元素都是表示子项目的路径的字符串。
//该方法会深度寻找,不限于当前层,也会查找package(如app,nib文件,RTFD文件)的内容,但非常好内存,一般避免使用。
- (NSArray *)subpathsOfDirectoryAtPath:(NSString *)path error:(NSError **)error NS_AVAILABLE(10_5, 2_0);
// 获取path路径下所有的子路径。与上一方法相同。
- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error NS_AVAILABLE(10_5, 2_0);
// 获取path的当前子路径(path下的所有直接子路径,不包括子路径的子路径)。path必须是一个目录。
- (NSData *)contentsAtPath:(NSString *)path;
// 获取文件内容,返回的是NSData类型的对象

- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error NS_AVAILABLE(10_5, 2_0);
// 只能创建文件夹,参数:path是要创建的文件夹路径,createIntermediates 为 YES 则表示允许系统自动创建中间的文件夹。
// 当指定路径中的文件夹都存在或者创建成功时,返回YES;否则返回NO 
// 代码说明中说“指定的文件夹在调用这方法前不能已经存在”,但在Xcode6.1中测试中如果已经存在则不重复创建但也不报错?
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error NS_AVAILABLE(10_5, 2_0);
// 拷贝文件,srcPath 为要拷贝的文件路径,dstPath 为目标文件路径。
// 如果目标目录已经存在同名文件,则无法拷贝。
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error NS_AVAILABLE(10_5, 2_0);
// 移动文件(剪切)。如果目标目录已经存在同名文件,则无法移动。
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error NS_AVAILABLE(10_5, 2_0);
// 删除文件
- (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data attributes:(NSDictionary *)attr;
// 在指定路径path(应该是个文件)创建文件,内容为data。
// NSData 是用来存储二进制字节数据的

2、以上方法实例操作如下:

        // 定义文件路径和文件夹路径
        NSString *path = @"/Users/yons/Desktop/f.txt";
        NSString *pathDir = @"/Users/yons/Desktop/";

        // 获取文件单例对象
        NSFileManager *fileManager = [NSFileManager defaultManager];

        // 判断文件(或文件夹)是否存在
        BOOL isExist = [fileManager fileExistsAtPath:path];
        // isExist == YES

        // 定义 isDir 来存储是否为文件夹(开始默认值为NO)
        BOOL isDir;
        // 判断是否存在
        BOOL isExist_2 = [fileManager fileExistsAtPath:pathDir isDirectory:&isDir];
        // isExist_2 == YES, isDir == YES

        // 判断是否可读
        BOOL isRead = [fileManager isReadableFileAtPath:pathDir];
        // isRead == YES

        // 判断是否可写
        BOOL isWrite = [fileManager isWritableFileAtPath:@"/usr/"];
        // isWrite == NO

        // 判断是否可执行
        BOOL isExe = [fileManager isExecutableFileAtPath:pathDir];
        // isExe == YES

        // 判断是否可删
        BOOL isDeletable = [fileManager isDeletableFileAtPath:path];
        // isDelete == YES

        // 获取属性信息
        NSDictionary *dict = [fileManager attributesOfItemAtPath:path error:nil];
        //NSLog(@"%@", dict);
        //  输出结果:
        // 
        //     (摘取部分)
        //     NSFileCreationDate = "2015-11-18 11:04:06 +0000";  //创建时间
        //     NSFileExtendedAttributes =     
        //         "com.apple.TextEncoding" = <7574662d 383b3133 34323137 393834>;
        //     ;
        //     NSFileExtensionHidden = 0;
        //     NSFileSize = 7;                  // 文件大小
        //     NSFileType = NSFileTypeRegular;  // 文件类型
        //     ......
        // 

        // 查找指定路径下的所有子路径
        NSArray *pathArr = [fileManager subpathsAtPath:pathDir];

        // 输出结果: pathArr = (
        //     "C:OC\\U8f85\\U52a9\\U56fe/0305.png",    //表示该路径下某图片的路径
        //     "C:OC\\U8f85\\U52a9\\U56fe/0306.png",
        //     "C:OC\\U8f85\\U52a9\\U56fe/0307.png",
        //     "C:OC\\U8f85\\U52a9\\U56fe/0401.png",
        //     "C:OC\\U8f85\\U52a9\\U56fe/0402.png",
        //     "C:OC\\U8f85\\U52a9\\U56fe/0403.png",
        //     "C:OC\\U8f85\\U52a9\\U56fe/0404.png",
        // )

        // 获取path的当前子路径
        NSArray *conOfDir = [fileManager contentsOfDirectoryAtPath:path error:nil];

        // 获取文件内容
        NSData *conAtPath = [fileManager contentsAtPath:path];
        NSLog(@"pathArr = %@", conAtPath);
        // 输出结果:pathArr = <77656c63 6f6d65> (原文件文本是“welcome”)

        // 依指定路径,创建文件夹
        NSString *newDir = @"/Users/yons/Desktop/newDir/test/";
        BOOL isCreateDir = [fileManager createDirectoryAtPath:newDir withIntermediateDirectories:YES attributes:nil error:nil];
        // isCreateDir == YES。在桌面文件夹下创建了newDir文件夹,该文件夹下又创建了test文件夹

        // 拷贝文件
        // 要拷贝的文件路径:
        NSString *sourcePath = @"/Users/yons/Desktop/f.txt";
        // 目标文件路径:
        NSString *goalPath = @"/Users/yons/Desktop/newDir/f_2.txt";
        BOOL isCopy = [fileManager copyItemAtPath:sourcePath toPath:goalPath error:nil];
        // isCopy == YES. 把桌面的 f.txt 文件 拷贝一份到 桌面的 newDir 文件夹中去,文件名为 f_2.txt

        // 移动文件(剪切)
        NSString *moveToPath = @"/Users/yons/Desktop/newDir/f_1.txt";
        BOOL isMove = [fileManager moveItemAtPath:sourcePath toPath:moveToPath error:nil];
        // isMove == YES。把桌面的 f.txt 文件移动到桌面的 newDir 文件夹中去,文件名为 f_1.txt

        // 删除文件
        BOOL isDelete = [fileManager removeItemAtPath:goalPath error:nil];
        // isDelete == YES. 把桌面的 newDir 文件夹中的 f_2.txt 文件删除了

        // 创建文件
        // 创建文件内容
        NSString *textOfFile = @"Hello World";
        // 把字符串转换为 NSData
        NSData *dataOfFile = [textOfFile dataUsingEncoding:NSUTF8StringEncoding];
        BOOL isWriteText = [fileManager createFileAtPath:path contents:textOfFile attributes:nil];
        // isWriteText == YES。在桌面新建了一个文件,文件名为 f.txt,内容为"Hello World"

3、NSFileManager文件下载思路:

二、copy & mutableCopy

copy,即复制或拷贝,是利用一个源对象产生一个副本对象。目的是:要使用某个对象的数据,但又要在修改对象的时候不影响原来的对象内容。
特点:修改源文件(或副本文件)的内容,不会影响副本文件(或源文件)。

复制分为:
浅拷贝:拷贝引用对象的指针。
深拷贝:拷贝引用对象的内容。

使用:用对象方法 copy 或 mutableCopy
copy方法: 创建的是不可变副本(如 NSString, NSArray, NSDictionary)
mutableCopy方法: 创建的是可变副本(如 NSMutableString, NSMutableArray, NSMutableDictionary)

使用copy方法的前提:需要遵守NSCopying协议、实现copyWithZone:方法

@protocol NSCopying
// NSCopying协议也只有这一个方法
- (id)copyWithZone:(NSZone *)zone;
@end

使用mutableCopy方法的前提:需要遵守NSMutableCopying协议、实现mutableCopyWithZone:方法

@protocol NSMutableCopying
//NSMutableCopying协议也只有这一个方法
- (id)mutableCopyWithZone:(NSZone *)zone;
@end

1、copy使用:
以NSString为例:

        NSString *str = [NSString stringWithFormat:@"year is %d", 2015];
        // str 地址:0x100114540. 堆区,不可变
        NSString *str_copy_s = [str copy];
        // str_copy_s 地址:0x100114540,与 str 相同,浅拷贝,不可变
        NSMutableString *str_copy_m = [str copy];
        // str_copy_m 地址:0x100114540,与 str 相同,浅拷贝,不可变
        NSString *str_mcopy_s = [str mutableCopy];
        // str_mcopy_s 地址:0x100114730,与 str 不相同,深拷贝,不可变
        NSMutableString *str_mcopy_m = [str mutableCopy];
        // str_mcopy_m 地址:0x100114770,与 str 不相同,深拷贝,可变

        // 引用计数器:str.retainCount == str_copy_s.retainCount == str_copy_m.retainCount == 3
        // 浅复制,源对象的引用计数器 + 1,相当于做了一个 retain 操作        

        NSMutableString *mstr = [NSMutableString stringWithFormat:@"month is %d", 11];
        // mstr 地址:0x100114810. 堆区,可变
        NSString *mstr_copy_s = [mstr copy];
        // mstr_copy_s 地址:0x186b255e163fdeb5,与 mstr 不相同,深拷贝,不可变
        NSMutableString *mstr_copy_m = [mstr copy];
        // mstr_copy_m 地址:0x186b255e163fdeb5,与 mstr 不相同,深拷贝,不可变
        NSString *mstr_mcopy_s = [mstr mutableCopy];
        // mstr_mcopy_s 地址:0x100114850,与 mstr 不相同,深拷贝,不可变
        NSMutableString *mstr_mcopy_m = [mstr mutableCopy];
        // mstr_mcopy_m 地址:0x1001148d0,与 mstr 不相同,深拷贝,可变

        // 引用计数器:mstr.retainCount == mstr_mcopy_s.retainCount == mstr_mcopy_m.retainCount == 1
        // 深复制,创建一个新对象,源对象、新对象的引用计数器都为1

所以 copy 用在NSString对象上,与 retain 用在其他 OC 对象上的效果类似。对比下:

使用注意:
(1)浅拷贝是复制一个对象的指针,会使计数器加1,所以也要用release将其减1
(2)深拷贝用在:要将一个对象从可变(不可变)转为不可变(可变)、或者将一个对象内容克隆一份的情况。

2、总结下 @property内存管理策略的选择:

3、 为自定义的类实现copy操作

NSObject类本身不遵守 NSCopying 或 NSMutableCopying 协议,它的子类要遵守 NSCopying 才能发送 copy消息,要遵守 NSMutableCopying 才能发送 mutableCopy 消息。

步骤:

//  Book.h
#import <Foundation/Foundation.h>
@interface Book : NSObject<NSCopying>   // 1.让类遵守 NSCopying 协议
// 两个实例变量
@property (nonatomic, assign) int page;
@property (nonatomic, copy) NSString *author;
@end
//  Book.m
#import "Book.h"
@implementation Book
-(id)copyWithZone:(NSZone *)zone   // 2.实现copyWithZone:方法
    // zone表示空间,但它是古老的技术,现在一般忽略它。

    // 创建一个新的对象,作为副本对象
    Book *book_copy = [[Book alloc] init];
    // 将源对象的实例变量一一赋值给副本对象的对应的实例变量
    book_copy.page = self.page;
    book_copy.author = self.author;

    //返回副本对象
    return book_copy;               // 3.返回一个数据与现有对象数据一样的新对象

@end
//  main.m
#import <Foundation/Foundation.h>
#import "Book.h"
int main(int argc, const char * argv[]) 
    @autoreleasepool 
        // 创建源对象book
        Book *book = [Book new];
        // 设置book 的实例变量
        book.page = 100;
        book.author = @"luxun";

        // 拷贝book到副本对象 book_2
        // 调用父类的 copy 方法,即调用实现了的 copyWithZone: 方法
        Book *book_2 = [book copy];

        // 输出源对象和副本对象的引用计数器和地址
        NSLog(@"%tu, %p", book.retainCount, book);
        NSLog(@"%tu, %p", book_2.retainCount, book_2);
     
    return 0;

//输出结果:
2015-11-18 16:19:11.718 CopyTest[1605:130941] 1, 0x100206fa0
2015-11-18 16:19:11.719 CopyTest[1605:130941] 1, 0x1002070c0
//注意到源对象book与副本对象book_2的retainCount都为1,两者的地址不相同。

如果要让类能进行mutableCopy操作,则步骤类似:

@interface Book : NSObject<NSMutableCopying>   // 1.让类遵守 NSMutableCopying协议
@end
@implementation Book
-(id)mutableCopyWithZone:(NSZone *)zone       // 2.实现mutableCopyWithZone:方法

    Book *book_mcopy = [[Book alloc] init];
    book_mcopy.page = self.page;
    book_mcopy.author = self.author;

    return book_mcopy;                         // 3.返回一个数据与现有对象数据一样的新对象

@end
//使用:
        Book *book = [Book new];
        Book *book_2 = [book copy];
        Book *book_3 = [book mutableCopy];

        NSLog(@"%tu, %p", book.retainCount, book);
        NSLog(@"%tu, %p", book_2.retainCount, book_2);
        NSLog(@"%tu, %p", book_3.retainCount, book_3);
//输出结果:
2015-11-18 16:34:08.525 CopyTest[1647:135370] 1, 0x1001106f0
2015-11-18 16:34:08.526 CopyTest[1647:135370] 1, 0x100114ba0
2015-11-18 16:34:08.526 CopyTest[1647:135370] 1, 0x100114cc0
//源对象和两种方法copy得到的副本对象的地址都是不同的

以上是关于Foundation框架 之 NSFileManager 与 copy & mutableCopy的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发之Foundation

Foundation框架 之 常见结构体包装数据日期

Foundation框架 之 常见结构体包装数据日期

Foundation框架 之 NSFileManager 与 copy & mutableCopy

Foundation框架 之 NSFileManager 与 copy & mutableCopy

《从零开始学Swift》学习笔记(Day 62)——Core Foundation框架之内存托管对象与非托管对象