ios进阶第一天(数据处理之文件读写)

本章包含的四个知识点:

  • 一、沙盒机制
  • 二、简单对象的写入与读取
  • 三、文件管理器
  • 四、复杂对象的写入与读取

一、 iOS沙盒(SandBox)机制

沙盒定义:每一个iOS应用程序都会为自己创建一个系统文件目录(文件夹),这个独立、封闭、安全的空间,叫做沙盒。

沙盒机制:1.沙盒是一种安全体系。2.它规定了应用程序只能在为该应用创建的文件夹(沙盒)内访问文件,不可以访问其他沙盒内的内容( iOS8已经部分开放访问)。3.所有的非代码文件都保存在这个地方,比如图片、声音、属性列表(plist)、sqlite数据库和文本文件等。
沙盒机制的特点:1.每个应用程序的活动范围都限定在自己的沙盒里。2.不能随意跨越自己的沙盒去访问别的应用程序沙盒中的内容(iOS8已经部分开始访问)3.应用程序向外请求或者接受数据都需要经过权限认证。

设置显示MAC隐藏文件的命令
defaults write com.apple.finder AppleShowAllFiles -bool true

应用程序的沙盒目录

应用程序的沙盒目录下会有三个文件夹Documents、Library(下面有Caches和Preferences目录)、tmp。

  • Documents:保存应用运行时生成的需要持久化的数据,ITunes会自动备份该目录。
  • Library/Caches:存放缓存文件,ITunes不会备份此目录,此目录下的文件不会在应用程序退出后删除,一般存放体积较大,不是特别重要的资源。
  • Library/Preferences:保存应用的所有偏好设置,iOS的Settings(设置) 应用会在该目录中查找应用的设置信息,iTunes会自动备份该目录,注意:您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好。
  • tmp:保存应用程序运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用程序没用运行时,系统也有可能会清除该目录下的文件,iTunes不会同步该目录。iPhone重启时,该目录下的文件会被删除。

获取应用程序的沙盒路径

  • 沙盒主路径:

NSString* homePath = NSHomeDirectory();
NSLog(@"%@",homePath);

- Documents的路径

//第一个参数:要找寻的文件夹的名称
//第二个参数:在那个文件夹下查找(查找的范围)
//第三个参数:是否展开文件路径(注意设置为NO的时候可能不能成功在该文件夹下写入文件)
NSArray* documentsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString* documentsPath = [documentsArray objectAtIndex:0];
NSLog(@"%@",documentsPath);

- Library路径

NSArray* libraryArray = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES);
NSString* libraryPath = [libraryArray objectAtIndex:0];
NSLog(@"%@",libraryPath);

- Caches路径

NSArray* cachesArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
NSString* cachesPath = [cachesArray objectAtIndex:0];
NSLog(@"%@",cachesPath);

- Preferences路径

NSString* preferencesPath = [libraryPath stringByAppendingString:@"/Preferences"];
//或者 注意一个有斜杠一个没有斜杠
//NSString* preferencesPath = [libraryPath stringByAppendingPathComponent:@"Preferences"];
NSLog(@"%@",preferencesPath);

- tmp路径

NSString* tmpPath = NSTemporaryDirectory();
NSLog(@"%@",tmpPath);

- NSUserDefaults的使用

//NSUserDefaults存储到Preferences文件夹下,在该文件夹下以plist格式存在,只能存储简单对象
[[NSUserDefaults standardUserDefaults] setObject:@"李四" forKey:@"name"];
NSDictionary* dic = @{@"特点":@"有才华,也有惊世容颜",@"age":@"22"};
[[NSUserDefaults standardUserDefaults] setObject:dic forKey:@"property"];
//删除
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"property"];
//获取存储的值
NSLog(@"%@",[[NSUserDefaults standardUserDefaults] objectForKey:@"name"]);



####二、简单对象的读写操作
iOS中的简单对象:1.NSString(字符串) 2.NSArray(数组) 3.NSDictionary(字典)  4.NSData(数据) {以上类型包括子类} 
注意:**数组(可变与不可变)和字典(可变与不可变)中元素对象的类型,也必须是以上四种,否则不能直接写入文件**
- *NSString的写入操作*

//将一段字符串写入documents文件夹下
NSArray* documentsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsPath = [documentsArray objectAtIndex:0];
//在上面的路径下创建一个文件路径,用来盛放文字
NSString* txtPath = [documentsPath stringByAppendingPathComponent:@"string.txt"];
//将文字写入文件中,当文字写入文件的时候,如果文件不存在,系统会先创建文件在写入,如果文件存在,就直接写入。新的内容会将原有的内容覆盖掉。
NSString* contentString = @"我是文本内容";
//写入操作
//第一个参数:文件路径
//第二个参数:原子性 YES:在写入过程中会产生一个临时文件,当写入完整后才会将内容移动到目标文件中 NO:直接在目标文件上进行操作,不产生临时文件,有可能损坏目标文件。
//第三个参数:字符串的编码格式
//第四个参数:如果写入出问题,这里会产生错误日志,一般不需要错误日志,直接赋值为nil,
//error对象用来保存错误日志
NSError* error = nil;
BOOL isSuccess = [contentString writeToFile: txtPath atomically:YES encoding:NSUTF8StringEncoding error:&error];
//只知道写入的结果,没有失败的细节描述
if (isSuccess) {
NSLog(@"写入成功");
}else{
NSLog(@"写入失败");
}
//有写入失败的细节描述
if (error) {
NSLog(@"写入文件出错%@",error.description);
}else{
NSLog(@"文件写入成功");
}

- *NSString的读取操作*

//要知道文件的路径
NSArray* documentsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsPath = [documentsArray objectAtIndex:0];

NSString* txtPath = [documentsPath stringByAppendingPathComponent:@"string.txt"];
//读取文件

NSError* error = nil;
NSString* readString  = [NSString stringWithContentsOfFile: txtPath encoding:NSUTF8StringEncoding error:&error];
if (!error) {
    NSLog(@"我是读出来的%@",readString);
}else{
    NSLog(@"%@",error.description);
}
- *NSArray(数组)的写入*

//路径
NSArray* docArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* docPath = [docArray objectAtIndex:0];
NSString* arrayPath = [docPath stringByAppendingPathComponent:@"array.plist"];
NSArray* dataArray = @[@"第一个",@"第二个",@"第三个",@"第四个"];
BOOL isSuccess = [dataArray writeToFile: arrayPath atomically:YES];

- *NSArray的读取*

//路径
NSString* arrayPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* filePath = [arrayPath stringByAppendingPathComponent:@"array.plist"];
NSArray* array = [[NSArray alloc] initWithContentsOfFile: filePath];
NSLog(@"%@",array);

- *NSDictionary(字典)的读写和数组类似*   
- *NSData的写入(将一张图片写入文件中)*

//路径
NSArray* pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* docPath = [pathArray objectAtIndex:0];
NSString* imagePath = [docPath stringByAppendingPathComponent:@"123.jpg"];
//图片写入
UIImage* image = [UIImage imageNamed:@"456.jpg"];
//将图片转换为NSData类型
//第二个参数为存储图片的品质
NSData* data = UIImageJPEGRepresentation(image, 1.0);
NSLog(@"%@",data);
BOOL isSuccess = [data writeToFile: imagePath atomically:YES];

- *NSData的读取*

//获取图片的路径
NSString* imagePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"123.jpg"];
//从图片路径中读取数据保存在NSData对象中
NSData* iamgeData = [[NSData alloc] initWithContentsOfFile: imagePath];
//将NSData转化为UIImage对象
UIImage* iamge = [UIImage imageWithData: iamgeData];

####三、文件管理器(NSFileManager)与文件对接(连接)器(NSFileHandle)
- **文件管理器**(NSFileManager)此类主要是对文件进行操作(创建、删除、改名)以及文件信息的获取。


 - 文件管理器的常用方法:                                                                                                                      
 
![NSFileManager类的常用方法.png](http://upload-images.jianshu.io/upload_images/3403981-6454bb7b2c73bc9c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


![NSFileManager类的常用方法2.png](http://upload-images.jianshu.io/upload_images/3403981-ec4b3cd716eabe01.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


  - 文件管理器的使用范例


注意:下面的代码获取沙盒路径是使用了SandBoxPaths这个类,这个自定义类封装了获取沙盒各个路径的方法。
//获取文件管理对象

NSFileManager* fileManager = [NSFileManager defaultManager];
//创建文件,并写入数据  如果该方法的返回值为YES,说明文件创建成功或者文件已经存在;NO:文件创建失败
//第一个参数:要创建的文件的路径
//第二个参数: 要写入文件的内容
//第三个参数:设置文件的用户组,权限,修改时间  ,如果设置为nil,系统会为该文件添加默认设置
//创建要写入的内容
NSArray* array = @[@"王",@"余",@"李",@"雷"];
//数组转为NSData
NSData* data = [NSJSONSerialization dataWithJSONObject:array options:NSJSONWritingPrettyPrinted error:nil];

BOOL isSuccess = [fileManager createFileAtPath:[[SandBoxPaths cachesPath] stringByAppendingPathComponent:@"array.json"] contents:data attributes:nil];

if (isSuccess) {
    NSLog(@"存入成功");
    
}else{
    NSLog(@"存入失败");
}

NSString* path = [[SandBoxPaths cachesPath] stringByAppendingPathComponent:@"array.json"];
//判断文件是否存在
if ([fileManager fileExistsAtPath:path]) {
    //文件存在
    //读取数据
    NSData* readData =  [fileManager contentsAtPath:[[SandBoxPaths cachesPath] stringByAppendingPathComponent:@"array.json"]];
    
    
    NSArray* readArray = [NSJSONSerialization JSONObjectWithData:readData options:NSJSONReadingAllowFragments error:nil];
    NSLog(@"%@",readArray);
}else{
    NSLog(@"该文件不存在");
}

// 将Caches中的array.json文件移动到Documents文件夹下
// 这里的移动相当于 剪切
NSString* newArrayPath = [[SandBoxPaths documentsPath] stringByAppendingPathComponent:@"newArray.json"];
BOOL isMove = [fileManager moveItemAtPath:path toPath:newArrayPath error:nil];

if (isMove) {
    NSLog(@"移动成功");
}else{
    NSLog(@"移动失败");
}



//这里的copy相当于复制,源文件还存在
 NSString* newArrayPath1 = [[SandBoxPaths documentsPath] stringByAppendingPathComponent:@"newArray1.json"];
NSError* error = nil;
BOOL isCopy = [fileManager copyItemAtPath:path toPath:newArrayPath1 error:&error];
if (isCopy) {
    NSLog(@"复制成功");
}else{
    NSLog(@"复制失败");
}
//判断文件内容是否一致
BOOL isEqual = [fileManager contentsEqualAtPath:path andPath:newArrayPath1];
if (isEqual) {
    NSLog(@"文件内容一致");
}else{
    NSLog(@"文件内容不同");
}
//删除文件

BOOL isRemove = [fileManager removeItemAtPath:path error:nil];
if (isRemove) {
NSLog(@"删除成功");
}else{
NSLog(@"删除失败");
}

//创建文件夹
NSString* dirPath = [[SandBoxPaths homePath]stringByAppendingPathComponent:@"NewDi/sd"];
//第一个参数:要创建的文件夹的路径
//第二个参数:YES:如果父目录不存在,创建的时候会将父目录一起创建,NO:如果父目录不存在,会创建失败
//第三个参数:文件夹的权限

    BOOL isCreate = [fileManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil];
    if (isCreate) {
        NSLog(@"创建成功");
    }else{
        NSLog(@"创建失败");
    }

- **文件连接器**(NSFileHandle)此类主要是对文件内容进行读取和写入操作。是非常基础的只针对文件内容的操作(写入,读取,更新),是把NSData,通过连接器一个字节一个字节的写入/读取文件。(NSData<-->NSFileHandle<-->文件):
 - 使用场景:对文件内容进行局部修改、追加内容。
 - 使用步骤:1.文件对接并获取一个NSFilehandle对象。2.读写操作。3.关闭连接。
 - 注意: NSFileHandle类并没有提供创建文件的功能。必须使用NSFileManager类的方法来创建文件。因此在使用此类的方法时,必须保证文件已经存在,否则返回nil。
 - 文件管理器的常用方法:

![NSFileHandle类的常用方法.png](http://upload-images.jianshu.io/upload_images/3403981-33ea00715e587d58.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


![NSFileHandle类的常用方法2.png](http://upload-images.jianshu.io/upload_images/3403981-c59fd2f36f141a36.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


- 文件连接器的使用范例


//判断某个路径下的文件是否存在,如果存在,直接存在,直接通过handle对象写入内容,如果不存在,先用manager对象创建文件,在操作文件

//
NSString* filePath = [[SandBoxPaths homePath] stringByAppendingPathComponent:@"NewDi/string.txt"];
//创建文件管理器对象
NSFileManager* fileManager = [NSFileManager defaultManager];
//判断该路径下的文件是否存在
if (![fileManager fileExistsAtPath:filePath]) {
        //文件不存在,先用文件管理器创建文件
    BOOL isCreate =  [fileManager createFileAtPath:filePath contents:nil attributes:nil];
    
    if (isCreate) {
        NSLog(@"创建成功");
    }else{
        NSLog(@"创建失败");
    }
}
//存在的话对文件内容进行直接操作
//打开一个文件准备写入内容
NSFileHandle* fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
//向文件写入内容
NSString* inputString = @"leilong";
NSData* inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
[fileHandle writeData:inputData];
//操作完毕,一定要关闭
[fileHandle closeFile];

//更新数据
NSFileHandle* updateFileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
//要从"龙"之后写入数据,就需要将文件偏移量设置为2,然后在开始写入。相当于从偏移量开始,将后面的内容进行了替换
[updateFileHandle seekToFileOffset:7];
//写入要增加的数据
NSString* addString = @"fei";
NSData* addData = [addString dataUsingEncoding:NSUTF8StringEncoding];
[updateFileHandle writeData:addData];
//关闭
[updateFileHandle closeFile];

//读取操作
NSFileHandle* readFileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
//读取所有

// NSData* readData = [readFileHandle readDataToEndOfFile];
//读取给定范围的数据
//先设置文件偏移量
[readFileHandle seekToFileOffset:3];
//设置要读取的长度
NSData* readData = [readFileHandle readDataOfLength:4];
NSString* readString = [[NSString alloc] initWithData:readData encoding:NSUTF8StringEncoding];
NSLog(@"%@",readString);
//关闭
[readFileHandle closeFile];


####四、复杂对象的读写
  - 复杂对象的定义:在Foundation框架内不存在的数据类:如自定义Person类无法在程序内通过writeToFile:           这个方法写入文件。


 - 如何将复杂对象写入文件?
    复杂对象无法通过writeToFile:方法进行进行数据持久化,只能通过将复杂对象转换为NSData(这个步骤就是归档),然后在通过writeToFile:写入文件

- 如何从文件中读取复杂对象?
 从文件中读取NSData数据,将NSData转换为复杂对象(这个步骤就是反归档)

- 归档与反归档示例:

//先自定义一个Person类,给两个属性 name(NSString*), age(NSInteger).然后遵循NSCoding协议,并且在 .m文件中实现NSCoding协议的两个协议方法。
//Person.h文件

import <Foundation/Foundation.h>

//要对复杂对象进行归档和反归档,复杂对象类必须要遵循 NSCoding 协议
@interface Person : NSObject<NSCoding>

@property(nonatomic,strong)NSString* name;
@property(nonatomic,assign)NSInteger age;
@end

//Person.m文件

import "Person.h"

@implementation Person
//这两个协议方法会在复杂对象归档和反归档的时候自动调用

//复杂对象的归档:相当于将复杂对象转换为NSData类型,是为了写入文件(持久化)

  • (void)encodeWithCoder:(NSCoder *)aCoder{
    //归档过程是将当前对象的每一个属性都进行编码
    //第一个参数:要进行编码的属性的值
    //第二个参数:为该参数的值加标记,加标记是为了解码(反归档)使用的
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.age forKey:@"age"];
    NSLog(@"调用了归档的方法");
    }
    //复杂对象的反归档:将NSData对象转化为复杂对象,是为了方便读取使用
    //也是一个初始化方法

  • (instancetype)initWithCoder:(NSCoder *)aDecoder{

    self = [super init];
    if (self) {
    //解码:相当于从文件中读取出来的值,赋值给相应的属性
    self.name = [aDecoder decodeObjectForKey:@"name"];
    self.age = [aDecoder decodeIntegerForKey:@"age"];

    }
    NSLog(@"调用了反归档的方法");

    return self;
    }
    @end


- 归档的过程

//需要创建一个Person对象进行归档
Person* person = [[Person alloc] init];
person.age = 1000;
person.name = @"李四";
//需要创建一个可变的data对象,用来存放归档好的person对象
NSMutableData* mData = [[NSMutableData alloc] init];
//还需要创建归档工具对象
//参数 为承接归档完成的复杂对象
NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mData];
//归档
//第一个参数:要归档的复杂对象
//第二个参数:标记,为了反归档使用
[archiver encodeObject:person forKey:@"person"];
//归档结束一定要调用下面的方法,要不然mData中不会有值
[archiver finishEncoding];

//将mData持久化到本地
NSString* filepath = [[SandBoxPaths homePath] stringByAppendingPathComponent:@"NewDi/archier.data"];

[mData writeToFile:filepath atomically:YES];

- 反归档的过程

//将刚才归档文件中的data数据读取出来,以便反归档使用
NSString* filePath = [[SandBoxPaths homePath] stringByAppendingPathComponent:@"NewDi/archier.data"];
NSData* data = [NSData dataWithContentsOfFile:filePath];
//初始化反归档工具
//参数:要反归档的data对象
NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person* perosn = [unarchiver decodeObjectForKey:@"person"];
//反归档结束的方法:(必须调用)
[unarchiver finishDecoding];
NSLog(@"name=%@,age=%ld",perosn.name,perosn.age);

推荐阅读更多精彩内容

  • 沙盒 Plist Preference偏好设置 NSKeyedArchiver归档 / NSKeyedUnarch...
    追风者366阅读 938评论 0 4
  • 应用沙盒 每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其他文件系统隔离。应用必须待在自己的沙盒...
    iOS_程序猿阅读 572评论 0 6
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 22,264评论 41 457
  • 概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,...
    Leeson1989阅读 527评论 4 1
  • 下面是我最近两年学习OC中的一些基础知识,对于学习OC基础知识的人可能有些帮助,拿出来分享一下,还是那句话不喜勿喷...
    小小赵纸农阅读 1,034评论 1 6