介绍所谓持久化就是将数据保存到硬盘中,使得之前保存的数据在应用程序或机器重启后可以继续访问。在iOS开发中,有很多数据持久化方案。接下来,我将尝试介绍5种解决方案:plist文件(属性列表)preference(偏好设置)NSKeyedArchiver(归档)SQLite3CoreData沙箱在介绍各种存储方法之前,有必要先解释一下下面的沙箱机制。默认情况下,iOS程序只能访问程序自己的目录,这个目录被称为“沙箱”。1.结构既然沙盒是一个文件夹,那我们看看里面都有什么。沙箱的目录结构如下:“应用程序包”DocumentsLibraryCachesPreferencestmp2。目录特性虽然沙盒中有这么多的文件夹,但是每个文件夹都是不同的,都有自己的特点。因此,在选择存储目录时,一定要慎重选择合适的目录。“应用程序包”:包含应用程序的源文件,包括资源文件和可执行文件。NSString*path=[[NSBundlemainBundle]bundlePath];NSLog(@"%@",path);Documents:最常用的目录。iTunes在同步app的时候会同步这个文件夹的内容,适合存放重要数据。NSString*path=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).firstObject;NSLog(@"%@",path);Library/Caches:iTunes不会同步这个文件夹,适合存放非重要数据,体积大,不需要备份。NSString*path=NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES).firstObject;NSLog(@"%@",path);Library/Preferences:iTunes在同步应用的时候,会同步这个文件夹的内容,一般保存设置信息的应用程序。tmp:iTunes不会同步这个文件夹,应用程序不运行的时候系统可能会删除这个目录下的文件,所以这个目录适合存放应用程序中的一些临时文件,用完了就删除。NSString*path=NSTemporaryDirectory();NSLog(@"%@",path);plist文件plist文件就是通过XML文件的方式在目录中保存一些具体的类。唯一可以序列化的类型如下:NSArray;NSMutableArray;NS词典;NSMutable字典;数据;NS可变数据;字符串;NS可变字符串;NSN号;NSDate;;NSString*fileName=[pathstringByAppendingPathComponent:@"123.plist"];2.存储NSArray*array=@[@"123",@"456",@"789"];[arraywriteToFile:fileNameatomically:YES];3.ReadNSArray*result=[NSArrayarrayWithContentsOfFile:fileName];NSLog(@"%@“,结果);4.注意只有上面列出的类型可以存储在plist文件中。存储时使用writeToFile:atomically:方法。其中,atomically表示是否需要先写入一个辅助文件,然后将辅助文件复制到目标文件地址。这是比较安全的写文件方式,一般写YES。读取时使用arrayWithContentsOfFile:方法。偏好1。如何使用//1。获取NSUserDefaults文件NSUserDefaults*userDefaults=[NSUserDefaultsstandardUserDefaults];//2.向文件写入内容[userDefaultssetObject:@"AAA"forKey:@"a"];[userDefaultssetBool:YESforKey:@"a"]"sex"];[userDefaultssetInteger:21forKey:@"age"];//2.1同步立即[userDefaultssynchronize];//3.读取文件NSString*name=[userDefaultsobjectForKey:@"a"];BOOLsex=[userDefaultsboolForKey:@"sex"];NSIntegerage=[userDefaultsintegerForKey:@"age"];NSLog(@"%@,%d,%ld",姓名,性别,年龄);2.注意preferences是专门用来保存应用程序配置信息的,一般不要在preferences中保存其他数据。如果不调用synchronize方法,系统会根据I/O情况随时保存到文件中。所以如果需要立即写入文件,就必须调用synchronize方法。首选项会将所有数据保存到同一个文件中。也就是preference目录下一个以这个应用包名命名的plist文件。#p#NSKeyedArchiver归档是iOS中序列化的另一种形式,只要遵循NSCoding协议的对象都可以通过它进行序列化。由于大多数支持存储数据的Foundation和CocoaTouch类都遵循NSCoding协议,因此对于大多数类来说归档相对容易。1.遵循NSCoding协议NSCoding协议声明了两个方法,这两个方法都必须实现。一个展示了如何将对象编码到存档中,另一个展示了如何取消存档以获取新对象。遵循协议并设置属性//1。遵循NSCoding协议@interfacePerson:NSObject//2.设置属性@property(strong,nonatomic)UIImage*avatar;@property(copy,nonatomic)NSString*name;@property(assign,nonatomic)NSIntegerage;@end实现协议方法//解包-(id)initWithCoder:(NSCoder*)aDecoder{if([superinit]){self.avatar=[aDecoderdecodeObjectForKey:@"avatar"];self.name=[aDecoderdecodeObjectForKey:@"name"];self.age=[aDecoderdecodeIntegerForKey:@"age"];}returnsself;}//Archive-(void)encodeWithCoder:(NSCoder*)aCoder{[aCoderencodeObject:self.avatarforKey:@"avatar"];[aCoderencodeObject:self.nameforKey:@"name"];[aCoderencodeInteger:self.ageforKey:@"age"];}特别注意如果要归档的类是自定义类的子类,需要在归档和解档之前,实现父类的归档和解档方法。即[superencodeWithCoder:aCoder]和[superinitWithCoder:aDecoder]方法;2、使用对象归档就是调用NSKeyedArchiver工厂方法archiveRootObject:toFile:方法。NSString*file=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).firstObjectstringByAppendingPathComponent:@"person.data"];Person*person=[[Personalloc]init];person.avatar=self.avatarView.image;person.name=self.nameField.text;person.age=[self.ageField.textintegerValue];[NSKeyedArchiverarchiveRootObject:persontoFile:file];要从文件中取消存档对象,请调用NSKeyedUnarchiverunarchiveObjectWithFile:的工厂方法。NSString*file=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).firstObjectstringByAppendingPathComponent:@"person.data"];Person*person=[NSKeyedUnarchiveunarchiveObjectWithFile:file];if(person){self.avatarView.image=person.avatar;自我.nameField.text=person.name;self.ageField.text=[NSStringstringWithFormat:@"%ld",person.age];}3.注意必须遵循NSCoding协议,可以指定保存文件的扩展名任意继承的时候先调用父类的归档和取消归档方法。SQLite3之前的所有存储方式都是覆盖存储。如果要添加一条数据,就必须读取整个文件,然后修改数据,然后将整个内容覆盖到文件中。所以它们都不适合存储大量内容。1、字段类型SQLite表面上将数据分为以下几种类型:integer:整数real:实数(浮点数)text:文本字符串blob:二进制数据,如文件、图片等。实际上,SQLite是无类型。即无论你在建表时指定什么字段类型,存储仍然可以存储任何类型的数据。而且在建表的时候也不需要指定字段类型。之所以SQLite的类型是为了良好的编程规范,方便开发者之间的交流,所以在使用的时候最好设置正确的字段类型!主键必须设置为integer2。准备工作准备工作是导入依赖库。在iOS中使用SQLite3,需要添加库文件:libsqlite3.dylib,并导入主头文件。这是一个C语言的库,所以直接用SQLite3还是比较麻烦的。3.使用创建并打开数据库,在操作数据库之前必须指定数据库文件和要操作的表。因此,要使用SQLite3,必须先打开数据库文件,然后指定或创建表。/***打开数据库并创建表*/-(void)openDatabase{//1.设置文件名NSString*filename=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).firstObjectstringByAppendingPathComponent:@"person.db"];//2.打开数据库文件,如果没有自动创建文件NSIntegerresult=sqlite3_open(filename.UTF8String,&_sqlite3);if(result==SQLITE_OK){NSLog(@"打开数据库成功!");//3.创建数据库表char*e??rrmsg=NULL;sqlite3_exec(_sqlite3,"CREATETABLEIFNOTEXISTSt_person(idintegerprimarykeyautoincrement,nametext,ageinteger)",NULL,NULL,&errmsg);if(errmsg){NSLog(@"Error:%s",errmsg);}else{NSLog(@"创建表successful!");}}else{NSLog(@"Failedtoopenthedatabase!");}}执行指令使用sqlite3_exec()方法执行任意SQL语句,如建表、更新、插入、删除等操作。但一般不用于执行查询语句,因为它不会返回查询到的数据。/***向表中插入1000条数据*/-(void)insertData{NSString*nameStr;NSIntegerage;for(NSIntegeri=0;i<1000;i++){nameStr=[NSStringstringWithFormat:@"Bourne-%d",arc4random_uniform(10000)];age=arc4random_uniform(80)+20;NSString*sql=[NSStringstringWithFormat:@"INSERTINTOt_person(name,age)VALUES('%@','%ld')",nameStr,age];char*e??rrmsg=NULL;sqlite3_exec(_sqlite3,sql.UTF8String,NULL,NULL,&errmsg);if(errmsg){NSLog(@"Error:%s",errmsg);}}NSLog(@"插入完成!");}查询命令上面提到了sqlite3_exec()方法一般不用来查询数据。因为查询数据必须得到查询结果,所以查询比较麻烦。示例代码如下:sqlite3_prepare_v2():检查sql的有效性sqlite3_step():逐行获取查询结果,重复直到最后一条记录sqlite3_coloum_xxx():获取对应类型的内容,iCol对应顺序SQL语句中字段的个数,从0开始。根据实际查询字段的属性,使用sqlite3_column_xxx获取对应的内容。sqlite3_finalize():releasestmt/***从表中读取数据到数组中*/-(void)readData{NSMutableArray*mArray=[NSMutableArrayarrayWithCapacity:1000];char*sql="selectname,agefromt_person;";sqlite3_stmt*stmt;NSIntegerresult=sqlite3_prepare_v2(_sqlite3,sql,-1,&stmt,NULL);if(result==SQLITE_OK){while(sqlite3_step(stmt)==SQLITE_ROW){char*name=(char*)sqlite3_column_text(stmt,0);NSIntegerage=sqlite3_column_int(stmt,1);//创建对象Person*person=[PersonpersonWithName:[NSStringstringWithUTF8String:name]Age:age];[mArrayaddObject:person];}self.dataList=mArray;}sqlite3_finalize(stmt);}4.总结一般来说,SQLite3的使用还是比较麻烦的,因为都是C语言的函数,比较难理解。但是在一般的开发过程中,都会使用到第三方开源库FMDB,它封装了这些基本的C语言方法,方便我们理解,提高开发效率。#p#FMDB1。简介FMDB是iOS平台的SQLite数据库框架。它以OC的形式封装了SQLite的C语言API。与cocoa自带的C语言框架相比,它有以下优点:使用起来更面向对象,省去了很多麻烦和冗余的C语言代码。与苹果自家的CoreData框架相比,更加轻量灵活。提供多线程安全的数据库操作方式,有效防止数据混乱。注:FMDB的gitHub地址2.核心类FMDB主要有3个类:FMDatabase一个FMDatabase对象代表一个独立的SQLite数据库,用于执行SQL语句FMResultSetFMDatabaseQueue用于在多线程中执行多个查询或更新是线程安全的3.打开数据库和c语言框架是一样的。FMDB通过指定SQLite数据库文件路径来创建FMDatabase对象,但是FMDB更容易理解和使用。使用前需要导入sqlite3.dylib。打开数据库的方法如下:NSString*path=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).firstObjectstringByAppendingPathComponent:@"person.db"];FMDatabase*database=[FMDatabasedatabaseWithPath:path];if(![databaseopen]){NSLog(@"打开数据库失败!");}值得注意的是,Path的值可以在以下三种情况下传递:具体文件路径,如果不存在则为空字符串@""会自动创建,并在临时目录Database中创建一个空的,当关闭FMDatabase连接时,数据库文件也被删除nil,会在内存中创建一个临时数据库,当关闭FMDatabase连接时,数据库将被销毁4.更新在FMDB中,除了查询之外的所有操作都称为“更新”,例如:创建、删除、插入、更新、删除等操作,使用executeUpdate:方法执行更新://有是三种常用方法:-(BOOL)executeUpdate:(NSString*)sql,...-(BOOL)executeUpdateWithFormat:(NSString*)format,...-(BOOL)executeUpdate:(NSString*)sqlwithArgumentsInArray:(NSArray*)arguments//例子[databaseexecuteUpdate:@"CREATETABLEIFNOTEXISTSt_person(idintegerprimarykeyautoincrement,nametext,ageinteger)"];//或者[databaseexecuteUpdate:@"INSERTINTOt_person(name,age)VALUES(?,?)",@"Bourne",[NSNumbernumberWithInt:42]];5。还有三种查询方法,使用起来相当简单:-(FMResultSet*)executeQuery:(NSString*)sql,...-(FMResultSet*)executeQueryWithFormat:(NSString*)format,...-(FMResultSet*)executeQuery:(NSString*)sqlwithArgumentsInArray:(NSArray*)arguments查询示例://1.执行查询FMResultSet*result=[databaseexecuteQuery:@"SELECT*FROMt_person"];//2.遍历结果集while([resultnext]){NSString*name=[resultstringForColumn:@"name"];intage=[resultintForColumn:@"age"];}6.线程安全在多个线程中使用一个FMDatabase实例是不明智的线程同时让多个线程共享同一个FMDatabase实例,它不能同时在多个线程中使用。如果一个FMDatabase实例同时在多个线程中使用,会造成数据混乱等问题。所以,请使用FMDatabaseQueue,它是线程安全的。下面是如何使用它:创建一个队列。FMDatabaseQueue*queue=[FMDatabaseQueuedatabaseQueueWithPath:aPath];使用队列[queueinDatabase:^(FMDatabase*database){[databaseexecuteUpdate:@"INSERTINTOt_person(name,age)VALUES(?,?)",@"Bourne_1",[NSNumbernumberWithInt:1]];[databaseexecuteUpdate:@"INSERTINTOt_person(姓名,年龄)VALUES(?,?)",@"Bourne_2",[NSNumbernumberWithInt:2]];[databaseexecuteUpdate:@"INSERTINTOt_person(name,age)VALUES(?,?)",@"Bourne_3",[NSNumbernumberWithInt:3]];FMResultSet*result=[databaseexecuteQuery:@"select*fromt_person"];while([resultnext]){}}];而且可以轻松地把简单的任务包安装到事务里:[queueinTransaction:^(FMDatabase*数据库,BOOL*rollback){[databaseexecuteUpdate:@"INSERTINTOt_person(name,age)VALUES(?,?)",@"Bourne_1",[NSNumbernumberWithInt:1]];[databaseexecuteUpdate:@"INSERTINTOt_person(name,age)VALUES(?,?)",@"Bourne_2",[NSNumbernumberWithInt:2]];[databaseexecuteUpdate:@"INSERTIINTOt_person(name,age)VALUES(?,?)",@"Bourne_3",[NSNumbernumberWithInt:3]];FMResultSet*result=[databaseexecuteQuery:@"select*fromt_person"];while([resultnext]){}//Rollback*rollback=YES;}];FMDatabaseQueue会在后台创建一个序列化的G-C-D队列,并执行你传递给G-C-D队列的block。这意味着你同时从多个线程调用调用方法,GDC也会按照它收到的块的顺序去执行。
