注册

ios-本地存储的五种方式

ios数据存储的5种方式

  1. NSUserDefaults(Preference偏好设置)
  2. plist存储
  3. 归档
  4. SQLite3
  5. CoreData

应用沙盒

Document:适合存储重要的数据, iTunes同步应用时会同步该文件下的内容,(比如游戏中的存档)
Library/Caches:适合存储体积大,不需要备份的非重要数据,iTunes不会同步该文件
Library/Preferences:通常保存应用的设置信息, iTunes会同步
tmp:保存应用的临时文件,用完就删除,系统可能在应用没在运行时删除该目录下的文件,iTunes不会同步

获取沙盒路径

Document:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFilePath = paths.firstObject;

NSuserDefault

NSuserDefault适合存储轻量级的本地数据,支持的数据类型有:NSNumber,NSString,NSDate,NSArray,NSDictionary,BOOL,NSData

沙盒路径为 Library/Preferences
文件格式为 .plist

优点:

  1. 不需要关心文件名
  2. 快速进行键值对存储
  3. 直接存储基本数据类型

缺点:

  1. 不能存储自定义数据
  2. 取出的数据都是不可变的
- (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,偷个懒还没验证

- (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);
}

归档

存储自定义对象

  1. 首先新建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];
}

- (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);
}

但是这种方法只能存储一个对象,存储多个对象要采用如下的方法:

- (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几乎相同的数据库语句,以及无需服务器即可使用的优点:

数据库的优点:

  1. 该方案可以存储大量的数据,存储和检索的速度非常快.
  2. 能对数据进行大量的聚合,这样比起使用对象来讲操作要快.

数据库的缺点:

  1. 它没有提供数据库的创建方式
  2. 它的底层是基于C语言框架设计的, 没有面向对象的API, 用起来非常麻烦
  3. 发杂的数据模型的数据建表,非常麻烦
    在实际开发中我们都是使用的是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()0];
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.生成上下文 关联模型文件生成数据库

1.创建模型文件

New File -> iOS -> Core Data ->Data Model

2.创建实体

Codegen

3.创建实体类

创建结果如图所示:

1.生成上下文 关联模型文件生成数据库,进行增删查改操作

#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()0];
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


转自:https://blog.csdn.net/u013712343/article/details/106698848

0 个评论

要回复文章请先登录注册