iOS文件操作和数据持久化总结

来源:互联网 发布:诡异 知乎 编辑:程序博客网 时间:2024/05/19 16:49

本文章是本人进几天学习和对前辈们一些文档的汇总,内容仅限于现阶段的理解,如有不严谨的地方望大神指正


文件操作 (NSFileManager)

#define PATH @"/Users/wanghua/Desktop/文件操作"

int main(int argc,constchar * argv[]) {

    @autoreleasepool {

        //FileManager管理文件

        NSFileManager * fm = [NSFileManagerdefaultManager];

       //单例对象,表示无论调用这个方法多少次,获得同一个对象。

       //default创建单例,表示我们使用这个单例的功能。

       //share创建单例,表示我们让整个工程共享单例中存储的数据。

查看某个目录下的文件

        NSError * error =nil;

        NSArray * array = [fmcontentsOfDirectoryAtPath:PATHerror:&error];    

        //查看目录下的文件,返回值是一个数组,数组中装着文件的名字

        //这个查看方式只会获取制定目录下的子目录和文件,不会获取子目录下的数据,因此称为浅度遍历

        if (error) {

            NSLog(@"%@", error);

            exit(0);   //终止进程

        }       

        NSLog(@"%@", array);

        

       //【深度遍历】

        array = [fmsubpathsOfDirectoryAtPath:PATHerror:&error];

        if (error) {

            NSLog(@"%@", error);

            exit(0);   //终止进程

        }        

        for (NSString * subPathin array) {

            NSLog(@"%@", subPath);

        }

判断一个文件是否存在,是否是目录

        if ([fmfileExistsAtPath:[PATHstringByAppendingPathComponent:@"test.txt"]] ==YES){

            NSLog(@"存在");

        } else {

            NSLog(@"不存在");

        }       

        BOOL isDirectory;

        if ([fmfileExistsAtPath:[PATHstringByAppendingPathComponent:@"文档"]isDirectory:&isDirectory] == YES) {

            //返回值为真,表示文件存在

            NSLog(@"文件存在");

            if (isDirectory ==YES) {

                NSLog(@"是目录");

            } else {

                NSLog(@"不是目录");

            }

        } else {

            NSLog(@"不存在");

        }

创建文件和目录

        //创建目录

        [fmcreateDirectoryAtPath:[PATHstringByAppendingPathComponent:@"test2"]withIntermediateDirectories:YESattributes:nilerror:&error];

        //第二个参数表示是否创建中间目录,如果传NO,缺少中间目录,方法会报错,创建失败。如果传YES,缺少中间目录,会自动创建。

        //第三个参数传入文件属性。一般传nil,表示默认属性。

        if (error) {

            NSLog(@"%@", error);

            exit(0);   //终止进程

        }        

        //创建文件

        BOOL ret = [fmcreateFileAtPath:[PATHstringByAppendingPathComponent:@"test2、file.test"]contents:[@"内容"dataUsingEncoding:NSUTF8StringEncoding]attributes:nil];

        if (ret ==NO) {

            perror("create");

            exit(-1);

        }        

        //第二个参数表示传入文件的内容。传nil表示暂不需要内容。

删除文件或目录

        [fm removeItemAtPath:[PATHstringByAppendingPathComponent:@"test2"]error:&error];

        if (error) {

            NSLog(@"%@", error);

            exit(0);   //终止进程

        }

 复制文件或目录

        [fm copyItemAtPath:[PATHstringByAppendingPathComponent:@"test2"]toPath:[PATHstringByAppendingPathComponent:@"DIR"]error:&error];

        //即使不改名,目的路径也必须写上文件的名字。

        if (error) {

            NSLog(@"%@", error);

            exit(0);   //终止进程

        }

 移动文件或目录

        [fm moveItemAtPath:[PATHstringByAppendingPathComponent:@"test2/file.txt"]toPath:[PATHstringByAppendingPathComponent:@"file2.txt"]error:&error];

        if (error) {

            NSLog(@"%@", error);

            exit(0);   //终止进程

        }

    }

    return0;

}

文件操作(NSFileHandle)        

        //以只读的方式打开文件,创建文件句柄。

        NSFileHandle * fh = [NSFileHandlefileHandleForReadingAtPath:PATH];       

        //读取数据,读取到文件尾

        NSData * data = [fhreadDataToEndOfFile];

        //字节长

        NSData * data1 = [fhreadDataOfLength:12];        

        //从新堆读写指针(定位读写的进度)进行设置

        [fhseekToFileOffset:6];        

        //再读12个字节

        NSData * data2 = [fhreadDataOfLength:12];       

        //同一个fileHandle,进行读取时,每次读取,接续这上一次的进度,继续读取。

        NSString * str = [[NSStringalloc]initWithData:dataencoding:NSUTF8StringEncoding];

        NSLog(@"%@", str);       

        NSString * str2 = [[NSStringalloc]initWithData:data2encoding:NSUTF8StringEncoding];

        //以只写的方式打开文件,创建文件句柄

        NSFileHandle * fh = [NSFileHandlefileHandleForWritingAtPath:PATH];

        NSData * data = [@"我是一只小小鸟,怎么飞也飞不高,我是一条小溪里的小鱼,可是我想做大海里的鲸鱼" dataUsingEncoding:NSUTF8StringEncoding];        

        //截断源文件

        [fhtruncateFileAtOffset:0];       

        //定位读写指针到文件尾

        [fh seekToEndOfFile];        

        //写入文件

        [fh writeData:data];

        //同一个文件句柄,写数据是追加的形式,但是每次创建fh是重写。但是并不销毁原有数据。




数据持久化:

数据持久(相对于数据存储于内存中)保存

为什么要进行数据持久化?

答:数据存储在内存中,当程序关闭,数据会随着程序关闭而释放,称为临时数据;

iOS有一套完整的数据安全体系,iOS应用程序只能访问自己的目录,这个目录成为沙盒目录,应用程序之间禁止数据的共享和访问

一、沙盒目录


沙箱目录是一种数据安全策略,很多系统都采用沙箱设计(如,Android系统)。沙箱目录设计原理是只能允许自己的应用访问目录,而不允许其他的应用访问。

NSString * path = NSHomeDirectory();

应用沙箱目录下有Documents、Library、tmp三个子目录。

  

Documents


该目录用于存储非常大的文件或需要非常频繁更新的数据,能够进行iTunes或Cloud的备份。我们可以通过下面的代码获取Documents的路径:

        NSString *DocumentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

Library

在Library目录下有PreferencePanes和Caches目录,其中前者用于存放应用程序的设置数据,后者与Documents相似,可以存放应用程序的数据,用来缓存文件。

Temp

tmp是临时文件目录,用户可以访问。它不能进行iTunes或iCloud的备份。

NSString * tempPath =NSTemporaryDirectory();


获取沙盒目录路径的方法

NSHomeDirectory-------------------->沙盒主路径

NSDocumentDirectory--------------->Document文件夹

NSLibraryDirectory------------------->Library文件夹

NSCachesDirectory------------------>Caches文件夹

NSTemporaryDirectory--------------->tem文件夹



数据持久化方式就是数据存储的方式,iOS支持本地存储和云端存储。本地存储主要有以下几种方式:


属性列表:集合对象可以读写到属性列表文件中。


对象归档:对象状态可以保存到归档文件中。


SQLite:开源嵌入式关系型数据库。


Core Data:一种对象关系映射技术(ORM),本质上也是通过SQLite存储的。


属性列表文件和对象归档一般用于存储少量数据。属性列表文件的访问要比对象归档的访问简单,Foundation框架集合对象都有对应的方法读写属性列表文件,对象归档是用NSData实现的。SQLite数据库和Core Data一般用于有几个简单表关系的大量数据的存储。



1、属性列表

属性列表是一种XML文件,Foundation框架中的数组和字典可以与属性列表文件相互转换。

          NSArray和NSDictionary提供了读写属性字典文件的方法。

         属性文件与NSArray的转化。

          +arrayWithContentsOfFile -initWithContentsOfFile 用于从属性文件中读取数据,创建NSArray.

         -writeToFile:atomically 把NSArray写入到属性文件。

        属性文件与NSDictionary的转化。

        +dictionaryWithContentsOfFile -initWithContentsOfFile 用于从属性文件中读取数据,创建NSDictionary.

        -writeToFile:atomically 把NSDictionary写入到属性文件。

2、对象归档


【NSKeyedAchiever NSKeyedUnAchiever】

【归档】将任何对象,或任何数据结构,转换为NSData类对象的过程,称为归档。也称数据的序列化。这种方式不适合大量数据和频繁读写的情况。

【NSKeyedAchiever】

【归档步骤】

1.需要归档的数据结构中,任何对象都必须遵从归档协议NSCoding.实现相关方法

归档过程是使用NSKeyedArchiver对象归档数据,具体过程为:先将归档数据写入NSData对象,然后在将NSData对象写入归档文件中。

【注】无需考虑NSString,NSArray,NSDictionary三个官方类,它们已经遵从过协议,实现方法了。

- (void)encodeWithCoder:(NSCoder *)aCoder;

- (id)initWithCoder:(NSCoder *)aDecoder;

2.使用KeyedAchiever进行归档

【解归档步骤】

解归档过程是从归档文件中读取数据到NSData对象,再利用NSKeyedUnarchiver对象从NSData对象中反归档出数据。

1.首先要确定你拥有同样的数据结构

2.使用KeyedUnAchiever进行解归档

实例:

        /*-----------归档----------*/

        NSMutableData * data = [[NSMutableDataalloc]init];

        //归档工具

        //创建一个关联data的归档工具

        NSKeyedArchiver * archiver = [[NSKeyedArchiveralloc]initForWritingWithMutableData:data];

        [archiver encodeObject:class forKey:@"class"];        

        [archiver finishEncoding];

        [data writeToFile:PATHatomically:YES];

        /*---------解归档---------*/

        NSData * data = [[NSData alloc] initWithContentsOfFile:PATH];       

        //创建关联data的解归档工具

        NSKeyedUnarchiver * unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

        //将对象按照key解归档

        PSBClass * class = [[unarchiver decodeObjectForKey:@"class"] retain];

        //需要retain,否则数据消失        

        [unarchiver finishDecoding];                

classModel:

#pragma mark - NSCoding协议方法

- (void)encodeWithCoder:(NSCoder *)aCoder

{

    [aCoder encodeObject:_arrayforKey:@"array"];

}

- (id)initWithCoder:(NSCoder *)aDecoder

{

    if (self = [superinit]) {

        //解归档过程中还原的数据都要retaincopy

        _array = [[aDecoderdecodeObjectForKey:@"array"]retain];

    }

    returnself;

}


studentModel:

#pragma mark - 实现NSCoding的方法

- (void)encodeWithCoder:(NSCoder *)aCoder

{

    //当学生被归档,会委托调用这个方法

    //在这个方法里,归档学生的属性

    //归档时需要设置一个key,方便解归档时查找数据。

    [aCoder encodeObject:self.nameforKey:@"name"];    

    [aCoder encodeObject:self.IDforKey:@"ID"];

    [aCoder encodeInteger:self.ageforKey:@"age"];

}

- (id)initWithCoder:(NSCoder *)aDecoder

{

    //解归档时调用

    if (self = [superinit]) {

        self.name = [aDecoderdecodeObjectForKey:@"name"];

        self.ID = [aDecoderdecodeObjectForKey:@"ID"];

        self.age = [aDecoderdecodeIntegerForKey:@"age"];

    }

    returnself;

}


3、SQLite是一个开源的嵌入式关系数据库,它在2000年由D. Richard Hipp发布,它的减少应用程序管理数据的开销,SQLite可移植性好,很容易使用,很小,高效而且可靠。SQLite 嵌入到使用它的应用程序中,它们共用相同的进程空间,而不是单独的一个进程。从外部看,它并不像一个RDBMS,但在进程内部,它却是完整的,自包含的数 据库引擎。 嵌入式数据库的一大好处就是在你的程序内部不需要网络配置,也不需要管理。因为客户端和服务器在同一进程空间运行。SQLite 的数据库权限只依赖于文件系统,没有用户帐户的概念。SQLite 有数据库级锁定,没有网络服务器。它需要的内存,其它开销很小,适合用于嵌入式设备。你需要做的仅仅是把它正确的编译到你的程序。SQLite是嵌入式系统中使用的关系数据库,目前主流版本是SQLite 3。SQLite运行时与使用它的应用程序之间共用相同的进程空间,而不是单独的两个进程。

SQLite提供了对SQL-92标准的支持,支持多表、索引、事物、视图和触发。SQLite是无数据类型的数据库,就是字段不用指定类型。

虽然SQLite可以忽略数据类型,但从编码规范上讲,应该在建表语句中指定数据类型,这样便于代码的阅读和理解。SQLite支持的常见数据类型如下:

INTEGER,有符号的整数类型。

REAL,浮点类型。

TEXT,字符串类型,采用UTF-8和UTF-16字符编码。

BLOB,二进制大对象类型,能够存放任何二进制数据。

SQLite提供的API比较多,文档对SQLite的用法以及一些API描述得很详细,实际使用过程中我们可以查阅文档。比如,在创建数据库时,我们需要经过如下3个步骤:

(1)使用sqlite3_open打开数据库;

(2)使用sqlite3_exec执行Create Table语句,创建数据表;

(3)使用sqlite3_close释放资源。


这些都是SQLite提供的C语言API,在OC中使用时需要注意数据类型的兼容问题。

其他像查询、删除等过程的调用,文档上都有详细的说明,这里就不赘述了。

这里还有一点需要注意,在使用SQLite之前,我们需要在工程中添加SQLite 3的库libsql3.dylib或libsql3.0.dylib。


简单基本的sql语句


(1) 数据记录筛选:

sql="select * from 数据表 where字段名=字段值 order by字段名 [desc]"  

sql="select * from 数据表 where字段名 like '%字段值%' order by字段名 [desc]"  

sql="select top 10 * from 数据表 where字段名=字段值 order by字段名 [desc]"  

sql="select top 10 * from 数据表 order by字段名 [desc]" 

sql="select * from 数据表 where字段名 in ('1','2','3')"  

sql="select * from 数据表 where字段名 between1 and2" 

(2) 更新数据记录:

sql="update 数据表 set字段名=字段值 where条件表达式"  

sql="update 数据表 set字段1=1,字段2=2 ……字段n=n where条件表达式"   

(3) 删除数据记录:  

sql="delete from 数据表 where条件表达式"  

sql="delete from 数据表" (将数据表所有记录删除)  

(4) 添加数据记录:  

sql="insert into 数据表 (字段1,字段2,字段3 …) values (1,2,3 …)"  

sql="insert into 目标数据表 select * from源数据表" (把源数据表的记录添加到目标数据表)  

(5) 数据记录统计函数:  

AVG(字段名)得出一个表格栏平均值  

COUNT(*;字段名)对数据行数的统计或对某一栏有值的数据行数统计  

MAX(字段名)取得一个表格栏最大的值  

MIN(字段名)取得一个表格栏最小的值  

SUM(字段名)把数据栏的值相加  

引用以上函数的方法:  

sql="select sum(字段名) as别名 from数据表 where条件表达式"  

//set rs=conn.excute(sql)  

rs("别名")获取统计的值,其它函数运用同上。  

查询去除重复值:select distinct * from table1  

(6) 数据表的建立和删除:

  CREATE TABLE 数据表名称(字段1类型1(长度),字段2类型2(长度) …… )



借助第三方库封装的简单数据库操作类

    

#import <Foundation/Foundation.h>

#import "FMDatabase.h"

// 可以根据模型自动建表,实现增删改查

// 模型名就是表明,模型属性名就是表的字段名

@interface WHDataBase : NSObject


+ (WHDataBase *)sharedDatabase;

// 创建所有的表(数组中保存的是所有模型的类名字符串)

- (void)createTable:(NSArray *)namesArray;

// 将一条记录添加到列表中

- (void)insertModel:(id)model;

// 将多条记录插入到表中

- (void)insertArray:(NSArray *)array;

// 从表中读取指定范围的数据,model表示要返回的对象类型,查询需要的参数,读取表信息,wheremodel的某一个属性名

- (NSArray *)selectArray:(NSInteger)startIndex count:(NSInteger)count model:(id)model where:(NSString *)where;

@end



#import "WHDataBase.h"

#import "FeedModel.h"

#import "NSObject+DHF.h"

@interface WHDataBase ()

{

    FMDatabase * _db;  

}

@end

@implementation WHDataBase


- (instancetype)init

{

    if (self = [superinit]) {

        NSString * path =NSHomeDirectory();

        path = [path stringByAppendingPathComponent:@"Documents/data.db"];

        NSLog(@"-----path:%@",path);

        // 借助第三方库创建数据库文件

        _db = [[FMDatabasealloc]initWithPath:path];

         // 尝试打开数据库

        if ([_dbopen]) {

            // 使用者必须要调一次此方法,传入需要创建的所有表名

            [selfcreateTable:nil];

        }

    }

    returnself;

}

// 根据model,返回和它匹配的字符串

- (NSString *)sqlFromModel:(id)model

{

    NSDictionary * dict = [modelpropertyList:NO];

    NSArray * keyArray = [dictallKeys];

    NSString * sql = [keyArraycomponentsJoinedByString:@" text,"];

    sql = [sql stringByAppendingString:@" text"];    

    return sql;

}

- (void)createTable:(NSArray *)namesArray

{

    // 数组中语句对应当前创建的一个table,如果创建多个,可以在数组中天剑别的table创建语句

    // NSArray * sqlArray = [NSArray arrayWithObjects:@"create table if not exists data (id integer PRIMARY KEY AUTOINCREMENT,%@)",nil];

    NSString * srcSql =@"create table if not exists %@ (id integer PRIMARY KEY AUTOINCREMENT,%@)";

    for (NSString * modelNamein namesArray) {

        // 根据模型的类名创建它的一个实例

        Class newClass = NSClassFromString(modelName);

        id model = [[newClassalloc]init];

        // 格式化此模型的建表语句

        NSString * sql = [NSStringstringWithFormat:srcSql,[modelNamelowercaseString],[selfsqlFromModel:model]];

        NSLog(@"xexcute sql:%@",sql);

        // fmdb常用的两个方法

        //executeUpdate 对应 建表,删除,修改,插入

        //executeQuery 对应查询

        // 这两个方法是变参的方法

        BOOL success = [_dbexecuteUpdate:sql];

        if (!success) {

            NSLog(@"创建表失败:%@",[_dblastErrorMessage]);

        }

    }

}


+ (WHDataBase *)sharedDatabase

{

    staticWHDataBase * db;

    if (!db) {

        db = [[[selfclass]alloc]init];

    }

    return db;

}

// 将一条记录添加到列表中

- (void)insertModel:(id)model

{

    // 格式化插入SQL语句

    NSString * sql =@"insert into %@ (%@) values (%@)";

    // 获得model属性对象的属性名和属性值组成的字典

    NSDictionary * dict = [modelpropertyList:YES];

    // 获得所有属性名数组

    NSArray * array = [dictallKeys];

    // 获得属性名组成的字符串都好分割

    NSString * nameList = [arraycomponentsJoinedByString:@","];

    // 格式化占位字符串

    NSMutableString * valueList = [NSMutableStringstring];

    for (NSInteger i =0; i < array.count; i++) {

        if (i ==0) {

            [valueList appendFormat:@"?"];

        }else{

            [valueList appendString:@",?"];

        }

    }

    

    sql = [NSStringstringWithFormat:sql,NSStringFromClass([modelclass]),nameList,valueList];

    

    NSLog(@"insert sql:%@",sql);

    BOOL success = [_dbexecuteUpdate:sqlwithArgumentsInArray:[dictallValues]];

    if (!success) {

        NSLog(@"插入失败:%@",[_dblastErrorMessage]);

    }

}

// 将多条记录插入到表中

- (void)insertArray:(NSArray *)array

{

    // 捕获异常

    @try {

        // 开始事务

        [_dbbeginTransaction];

        for (id modelin array) {

            [selfinsertModel:model];

        }

    }

    @catch (NSException *exception) {

        // 回滚数据(此次提前全部取消)

        [_dbrollback];

    }

    @finally {

        // 提交数据,最后统一提交,提高效率

        [_dbcommit];

    }

}

// 从表中读取指定范围的数据

- (NSArray *)selectArray:(NSInteger)startIndex count:(NSInteger)count model:(id)model where:(NSString *)where

{

    NSMutableArray * array = [NSMutableArrayarray];

    NSString * sql =@"select %@ from %@";

    // 如果有条件查询

    if (where) {

        sql = [sql stringByAppendingFormat:@" where%@=?",where];

    }

    // 如果需要读取限制

    if (count !=0) {

        sql = [sql stringByAppendingFormat:@" limit %d,%d",startIndex,count];

    }

    

    NSDictionary * dict = [modelpropertyList:NO];

    // 获得查询的字段列表字符串

    NSString * nameList = [[dictallKeys]componentsJoinedByString:@","];

    // 格式化查询语句

    sql = [NSStringstringWithFormat:sql,nameList,NSStringFromClass([modelclass])];

    NSLog(@"select sql:%@",sql);

    

    

    // 执行查询,结果为FMResultSet的对象

    FMResultSet * rs;

    if (where) {

        rs = [_dbexecuteQuery:sql,[modelvalueForKey:where]];

    }else{

        rs = [_dbexecuteQuery:sql];

    }

    // 遍历结果集,将每条记录存入model

    while ([rsnext]) {

        // 根据传入的模型创建相同类型对象

        id newModel = [[[modelclass]alloc]init];

        // 获得当前记录数据字典

        NSDictionary * dict = [rsresultDictionary];

        // 保存数据到模型对象中(kvc的方法)

        [newModel setValuesForKeysWithDictionary:dict];


        [array addObject:newModel];

    }

    if (array.count ==0) {

        NSLog(@"读取失败:%@",[_dblastErrorMessage]);

    }

    return array;

}

@end

4.Core Data
Core Data本质上是使用SQLite保存数据,但是它不需要编写任何SQL语句。


要使用Core Data,需要在Xcode中的数据模型编辑器中设计好各个实体以及定义好他们的属性和关系。之后,通过操作这些对象,结合Core Data完成数据的持久化:


NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSError *error;
NSString *fieldName = [NSString stringWithFormat:@"test%d", i];
UITextField *theField = [self valueForKey:fieldName];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//创 建描述语句,需求Line对象。类似于在数据库中限定为Line表。
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"  inManagedObjectContext:context];
[request setEntity:entityDescription];
//创建限制性语句,类似于SQL语句中的 where lineNum = i
NSPredicate *pred = [NSPredicate predicateWithFormat:@"(lineNum = %d)", i];
[request setPredicate:pred];
NSManagedObject *theLine = nil;
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil){
NSLog(@”There was an error!”);
// Do whatever error handling is appropriate
}
if ([objects count] > 0){    //如果符合条件的object存在,则取出
theLine = [objects objectAtIndex:0];
}
else {  //如果不存在,则插入一个新的.
theLine = [NSEntityDescription insertNewObjectForEntityForName:@"Line"
inManagedObjectContext:context];
[theLine setValue:[NSNumber numberWithInt:i] forKey:@”lineNum”];  //设置这个object的属性,coredata会自动将其写入sqlite
[theLine setValue:theField.text forKey:@"lineText"];
[request release];

}


下面是其取数据的过程:


Core_Data_PersistenceAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil)
{
NSLog(@”There was an error!”);
// Do whatever error handling is appropriate
}
//每一个对象在CoreData中都表示为一个NSManagedObject对象(类似于数据库表中的每一行),他的属性通过键/值 方式获取
for (NSManagedObject *oneObject in objects)
{
NSNumber *lineNum = [oneObject valueForKey:@"lineNum"];
NSString *lineText = [oneObject valueForKey:@"lineText"];
}
[request release];




0 0
原创粉丝点击