ios数据存储的5种方式
NSUserDefaults(Preference偏好设置)
plist存储
归档
SQLite3
CoreData
应用沙盒
Document:适合存储重要的数据, iTunes同步应用时会同步该文件下的内容,(比如游戏中的存档)
Library/Preferences:通常保存应用的设置信息, 系统可能在应用没在运行时删除该目录下的文件,iTunes会同步Library/Caches:适合存储体积大,不需要备份的非重要数据,iTunes不同步该文件
tmp:保存应用的临时文件,用完就删除,系统可能在应用没在运行时删除该目录下的文件,iTunes不同步该文件
获取沙盒路径
Document:
//Document path NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; //Library path NSString *libraryPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0]; //caches path NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; //Preferences path NSString*preferencePath = [libraryPath stringByAppendingString:@"/Preferences"]; //tem path NSString *tmpPath = NSTemporaryDirectory(); //app path NSString *appPath = [[NSBundle mainBundle] bundlePath];
NSuserDefault
NSuserDefault适合存储轻量级的本地数据,支持的数据类型有:NSNumber,NSString,NSDate,NSArray,NSDictionary,BOOL,NSData
沙盒路径为 Library/Preferences
文件格式为 .plist
优点:
不需要关心文件名
快速进行键值对存储
直接存储基本数据类型
缺点:
1.不能存存取自定义数据类型,如果需要支持,需要自定义数据转为支持的类型;比如实现NSCoding协议的encodeWithCoder、initWithCoder。
- (IBAction)userDefaultSave:(id)sender { NSArray *testArray = @[@"test1", @"test2", @"test3"]; [[NSUserDefaults standardUserDefaults] setObject:testArray forKey:@"arrayKey"]; [[NSUserDefaults standardUserDefaults] synchronize]; }
- (IBAction)userDefaultLoad:(id)sender { NSArray *testArray = [[NSUserDefaults standardUserDefaults] objectForKey:@"arrayKey"]; NSLog(@"%@", testArray); }
plist存储
plist支持的数据类型:
NSArray; NSMutableArray; NSDictionary; NSMutableDictionary; NSData; NSMutableData; NSString; NSMutableString; NSNumber; NSDate; 不支持BOOL 而且最外层好像要用`NSArray 或 NSDictionary,主要是使用writeToFile api写入指定目录
- (IBAction)plistSave:(id)sender { NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; NSString *filePath = [cachePath stringByAppendingPathComponent:@"testPlist.plist"]; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:@"ran" forKey:@"name"]; [dict setObject:@"18" forKey:@"age"]; [dict writeToFile:filePath atomically:YES]; } - (IBAction)plistLoad:(id)sender { NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; NSString *filePath = [cachePath stringByAppendingPathComponent:@"testPlist.plist"]; NSDictionary *t = [NSDictionary dictionaryWithContentsOfFile:filePath]; NSLog(@"%@",t); }
归档
存储自定义对象
首先新建Person类,并遵守NSCoding协议
@interface Person : NSObject<NSCoding> @property(nonatomic, strong)NSString *name; @property(nonatomic, strong)NSString *age; @end
实现协议方法
@implementation Person - (instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { _name = [coder decodeObjectForKey:@"name"]; _age = [coder decodeObjectForKey:@"age"]; } return self; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.name forKey:@"name"]; [coder encodeObject:self.age forKey:@"age"]; } @end
归档解档
- (IBAction)archive:(id)sender { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentFilePath = paths.firstObject; NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"]; Person *p1 = [[Person alloc] init]; p1.name = @"ran"; p1.age = @"18"; [NSKeyedArchiver archiveRootObject:p1 toFile:filePath]; //也可以使用这种方式写到Preferences personModel目录 //NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; //NSData *data = [NSKeyedArchiver archivedDataWithRootObject:p1]; //[userDefaults setObject:data forKey:@"personModel"]; //[userDefaults synchronize]; } - (IBAction)unarchive:(id)sender { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentFilePath = paths.firstObject ; NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"]; Person *p1 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath] ; NSLog(@"%@", p1.name); NSLog(@"%@", p1.age); //也可以使用这种方式取到Preferences personModel目录 //NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; //Person *p1 = (Person *)[NSKeyedUnarchiver unarchiveObjectWithData:[userDefaults objectForKey:@"personModel"]]; //NSLog(@"%@", p1.name); //NSLog(@"%@", p1.age); }
但是这种方法只能存储一个对象,存储多个对象要采用如下的方法:
- (IBAction)archiveManyObject:(id)sender { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentFilePath = paths.firstObject ; NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"]; NSMutableData *data = [[NSMutableData alloc] init]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //将数据区连接到NSKeyedArchiver对象 Person *p1 = [[Person alloc] init]; p1.name = @"ran1"; p1.age = @"18"; [archiver encodeObject:p1 forKey:@"person1"]; Person *p2 = [[Person alloc] init]; p2.name = @"ran2"; p2.age = @"19"; [archiver encodeObject:p2 forKey:@"person2"]; [archiver finishEncoding]; [data writeToFile:filePath atomically:YES]; } - (IBAction)unarchiveManyObject:(id)sender { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentFilePath = paths.firstObject ; NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"]; NSData *data = [NSData dataWithContentsOfFile:filePath]; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; Person *p1 = [unarchiver decodeObjectForKey:@"person1"]; Person *p2 = [unarchiver decodeObjectForKey:@"person2"]; [unarchiver finishDecoding]; NSLog(@"%@", p1.name); NSLog(@"%@", p2.name); }
SQLite3
数据库(splite):
splite是一个轻量级,跨平台的小型数据库,可移植性比较高,有着和MySpl几乎相同的数据库语句,以及无需服务器即可使用的优点:
数据库的优点:
该方案可以存储大量的数据,存储和检索的速度非常快.
能对数据进行大量的聚合,这样比起使用对象来讲操作要快.
数据库的缺点:
它没有提供数据库的创建方式
它的底层是基于C语言框架设计的, 没有面向对象的API, 用起来非常麻烦
发杂的数据模型的数据建表,非常麻烦
在实际开发中我们都是使用的是FMDB第三方开源的数据库,该数据库是基于splite封装的面向对象的框架.
#import "SqliteVC.h" #import "Person.h" @interface SqliteVC() { sqlite3 *_db; } @end @implementation SqliteVC - (void)viewDidLoad { [super viewDidLoad]; NSString *fileName = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"]; NSLog(@"fileName = %@",fileName); int result = sqlite3_open(fileName.UTF8String, &_db); //创建(打开)数据库,如果数据库不存在,会自动创建 数据库文件的路径必须以C字符串(而非NSString)传入 if (result == SQLITE_OK) { NSLog(@"成功打开数据库"); char *errorMesg = NULL; const char *sql = "create table if not exists t_person (id integer primary key autoincrement, name text, age integer);"; int result = sqlite3_exec(_db, sql, NULL, NULL, &errorMesg); //sqlite3_exec()可以执行任何SQL语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据 if (result == SQLITE_OK) { NSLog(@"成功创建t_person表"); } else { NSLog(@"创建t_person表失败:%s",errorMesg); } } else { NSLog(@"打开数据库失败"); } } - (IBAction)insert:(id)sender { for (int i = 0; i < 30; i++) { NSString *name = [NSString stringWithFormat:@"person-%d",arc4random()%100]; int age = arc4random() % 100; char *errorMesg = NULL; NSString *sql = [NSString stringWithFormat:@"insert into t_person (name,age) values ('%@',%d);",name, age]; int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg); if (result == SQLITE_OK) { NSLog(@"添加数据成功"); } else { NSLog(@"添加数据失败"); } } } - (IBAction)delete:(id)sender { char *errorMesg = NULL; NSString *sql = @"delete from t_person where age >= 0"; int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg); if (result == SQLITE_OK) { NSLog(@"删除成功"); }else { NSLog(@"删除失败"); } } - (IBAction)query:(id)sender { const char *sql = "select id, name, age from t_person;"; //"select id, name, age from t_person where age >= 50;" sqlite3_stmt *stmt = NULL; //定义一个stmt存放结果集 int result = sqlite3_prepare_v2(_db, sql, -1, &stmt, NULL); //检测SQL语句的合法性 if (result == SQLITE_OK) { NSLog(@"查询语句合法"); while (sqlite3_step(stmt) == SQLITE_ROW) { int ID = sqlite3_column_int(stmt, 0); const unsigned char *sname = sqlite3_column_text(stmt, 1); NSString *name = [NSString stringWithUTF8String:(const char *)sname]; int age = sqlite3_column_int(stmt, 2); NSLog(@"%d %@ %d",ID, name, age); } } else { NSLog(@"查询语句非法"); } } - (IBAction)update:(id)sender { NSString *sql = @"update t_person set name = '哈哈' where age > 60"; char *errorMesg = NULL; int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg); if (result == SQLITE_OK) { NSLog(@"更改成功"); }else { NSLog(@"更改失败"); } }
coreData
coreData是苹果官方在iOS5之后推出的综合性数据库,其使用了对象关系映射技术,将对象转换成数据,将数据存储在本地的数据库中
coreData为了提高效率,需要将数据存储在不同的数据库中,比如:在使用的时候,最好是将本地的数据保存到内存中,这样的目的是访问速度比较快.
CoreData与SQLite进行对比
SQLite
1、基于C接口,需要使用SQL语句,代码繁琐
2、在处理大量数据时,表关系更直观
3、在OC中不是可视化,不易理解
CoreData
1、可视化,且具有undo/redo能力
2、可以实现多种文件格式:
* NSSQLiteStoreType
* NSBinaryStoreType
* NSInMemoryStoreType
* NSXMLStoreTyp
3、苹果官方API支持,与iOS结合更紧密
CoreData核心类与结构
NSManagedObjectContext(数据上下文)
对象管理上下文,负责数据的实际操作(重要)
作用:插入数据,查询数据,删除数据,更新数据
NSPersistentStoreCoordinator(持久化存储助理)
相当于数据库的连接器
作用:设置数据存储的名字,位置,存储方式,和存储时机
NSManagedObjectModel(数据模型)
数据库所有表格或数据结构,包含各实体的定义信息
作用:添加实体的属性,建立属性之间的关系
操作方法:视图编辑器,或代码
NSManagedObject(被管理的数据记录)
数据库中的表格记录
NSEntityDescription(实体结构)
相当于表格结构
NSFetchRequest(数据请求)
相当于查询语句
后缀为.xcdatamodeld的包
里面是.xcdatamodel文件,用数据模型编辑器编辑
编译后为.momd或.mom文件
类关系图
开始创建coredata
步骤:
1.创建模型文件 [相当于一个数据库]
2.添加实体 [一张表]
3.创建实体类 [相当模型--表结构]
4.生成上下文 关联模型文件生成数据库
创建模型文件
New File -> iOS -> Core Data ->Data Model
创建实体
创建实体
Codegen
创建实体类
创建实体类
创建结果如图所示:
创建结果
生成上下文 关联模型文件生成数据库,进行增删查改操作
#import "coredataVC.h" #import <CoreData/CoreData.h> #import "Student+CoreDataProperties.h" @interface coredataVC () @property(nonatomic, strong)NSManagedObjectContext *context; @end @implementation coredataVC - (void)viewDidLoad { [super viewDidLoad]; //entity 记得勾选 language:objective-c 和 codegen:manual/none [self createSql]; } - (void)createSql { //获取模型路径 NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Person" withExtension:@"momd"]; //根据模型文件创建模型对象 NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; //利用模型对象创建持久化存储助理 NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; //数据库的名称和路径 NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *sqlPath = [docStr stringByAppendingPathComponent:@"coreData.sqlite"]; NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath]; NSLog(@"数据库 path = %@", sqlPath); NSError *error = nil; //设置数据库相关信息 添加一个持久化存储库并设置类型和路径,NSSQLiteStoreType:SQLite作为存储库 [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:&error]; if (error) { NSLog(@"添加数据库失败:%@",error); } else { NSLog(@"添加数据库成功"); } //3、创建上下文 保存信息 对数据库进行操作 关联持久化助理 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; context.persistentStoreCoordinator = store; _context = context; } 增 - (IBAction)insertClick:(id)sender { Student * student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:_context]; student.name = [NSString stringWithFormat:@"stu-%d",arc4random()%100]; student.age = arc4random()%30; NSError *error = nil; if ([_context save:&error]) { NSLog(@"数据插入到数据库成功"); }else{ NSLog(@"数据插入到数据库失败"); } } 删 - (IBAction)deleteClick:(id)sender { //创建删除请求 NSFetchRequest *deleRequest = [NSFetchRequest fetchRequestWithEntityName:@"Student"]; //删除条件 没有任何条件就是读取所有的数据 //NSPredicate *pre = [NSPredicate predicateWithFormat:@"age < %d", 10]; //deleRequest.predicate = pre; //返回需要删除的对象数组 NSArray *deleArray = [_context executeFetchRequest:deleRequest error:nil]; //从数据库中删除 for (Student *stu in deleArray) { [_context deleteObject:stu]; } NSError *error = nil; if ([_context save:&error]) { NSLog(@"删除数据成功"); }else{ NSLog(@"删除数据失败, %@", error); } } 查 - (IBAction)queryClick:(id)sender { //创建查询请求 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"]; //查询条件 没有任何条件就是读取所有的数据 NSPredicate *pre = [NSPredicate predicateWithFormat:@"age >= 0"]; request.predicate = pre; // 从第几页开始显示 通过这个属性实现分页 //request.fetchOffset = 0; // 每页显示多少条数据 //request.fetchLimit = 6; //发送查询请求 NSArray *resArray = [_context executeFetchRequest:request error:nil]; //打印查询结果 for (Student *stu in resArray) { NSLog(@"name=%@, age=%d",stu.name, stu.age); } } 改 - (IBAction)updateClick:(id)sender { //创建查询请求 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"]; NSPredicate *pre = [NSPredicate predicateWithFormat:@"age >= 0"]; request.predicate = pre; //发送请求 NSArray *resArray = [_context executeFetchRequest:request error:nil]; //修改 for (Student *stu in resArray) { stu.name = @"ran"; } NSError *error = nil; if ([_context save:&error]) { NSLog(@"更新数据成功"); }else{ NSLog(@"更新数据失败, %@", error); } } @end
相关推荐
0评论