数据存储

2017-01-12  本文已影响11人  CoderLNHui

iOS应用数据存储的常用方式

1. Plist存储(属性列表)

// 1.Plist存储(生成plist文件)
// 在Plist文件中不能保存自定义对象*+
- (IBAction)saveBtn:(UIButton *)sender {
    // 参数:搜索的目录,搜索的范围,是否展开路径
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSLog(@"%@",path);

    // 1.把数组保存到沙盒
    NSArray *dataArray = @[@"LN",@10];
    NSString *filePath = [path stringByAppendingPathComponent:@"data.plist"];
    NSLog(@"%@",filePath);

    // 写入沙盒路径
    [dataArray writeToFile:filePath atomically:YES];

    // 2.把字典保存到沙盒
    NSDictionary *dict = @{@"name":@"LN",@"age":@10};
    NSString *dictPath = [path stringByAppendingPathComponent:@"dict.plist"];
    [dict writeToFile:dictPath atomically:YES];
}

// 2.Plist提取
- (IBAction)readBtn:(UIButton *)sender {
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *filePath = [path stringByAppendingPathComponent:@"data.plist"];
    NSArray *dataArray = [NSArray arrayWithContentsOfFile:filePath];
    NSLog(@"%@",dataArray);

    NSString *filePath1 = [path stringByAppendingPathComponent:@"dict.plist"];
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath1];
    NSLog(@"%@",dict);
}

2. preference(偏好设置)


#define LNname @"name"
#define LNage @"age"

// 1.偏好设置存储(key存取,最好定义成宏以防写错)
// 偏好设置不能保存自定义对象*+
- (IBAction)saveBtn:(UIButton *)sender {
    // NSUserDefaults保存也是plist文件
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:@"LN" forKey:LNname];
    [defaults setObject:@10 forKey:LNage];
    
    // 写入文件当中
    [defaults synchronize];
    NSLog(@"%@",NSHomeDirectory());
}

// 2.偏好设置提取
- (IBAction)readBtn:(UIButton *)sender {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *name = [defaults objectForKey:LNname];
    NSInteger age = [defaults integerForKeyLNage];
    
    NSLog(@"%@ %ld",name,age);
}

3. NSKeyedArchiver归档(NSCoding协议)

Person


//  Person.h
#import <Foundation/Foundation.h>
@class Dog;
@interface Person : NSObject<NSCoding>

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, strong) Dog *dog;
@end

//  Person.m
#import "Person.h"
@implementation Person

// 归档存储会调用(在保存对象时要告诉保存对象那些属性)
- (void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInt:self.age forKey:@"age"];
    [aCoder encodeObject:self.dog forKey:@"dog"];
}

// 归档提取(解析文件的时候)会调用,(告诉当前要解析文件当中哪些属性)
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
    /*
        当只有遵守了NSCoding协议时,才有[super initWithCoder]
    @interface Person : NSObject (父类NSObject没有遵守NSCoding协议,继承UIView就可以)
    */
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.age = [aDecoder decodeIntForKey:@"age"];
        self.dog = [aDecoder decodeObjectForKey:@"dog"];
    }
    return self;
}

// 归档
// 保存自定义对象(模型类要遵守<NSCoding>协议)*+
- (IBAction)saveBtn:(UIButton *)sender {Person *person = [[Person alloc] init];
    person.name = @"LN";
    person.age = 10;

    Dog *dog = [[Dog alloc] init];
    dog.name = @"xiao Hui";
    person.dog = dog;
    
    // 获取沙盒目录
    NSString *tmpPath = NSTemporaryDirectory();
    NSString *filePath = [tmpPath stringByAppendingPathComponent:@"Person.data"];
    NSLog(@"%@",NSTemporaryDirectory());
        
    // 归档(archiveRootObject会调用encodeWithCoder:)
    [NSKeyedArchiver archiveRootObject:person toFile:filePath];
}


// 2.归档提取
- (IBAction)readBtn:(UIButton *)sender {
    // 获取沙盒目录
    NSString *tmpPath = NSTemporaryDirectory();
    NSString *filePath = [tmpPath stringByAppendingPathComponent:@"Person.data"];
    
    //归档提取 unarchiveObjectWithFile会调用initWithCoder:
    Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    NSLog(@"%@--%@",person.name,person.dog.name);
}
//从xib当加载的时候 (是加载完毕时调用)
-(void)awakeFromNib{
    NSLog(@"awakeFromNib==%@",self.btn);
    // awakeFromNib==<UIButton: 0x7ffd08c1e450; frame = (100 153; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7ffd08c1af60>>
}

//当解析一个文件的时候会调用initWithCoder (一开始加载Xib就调用)
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if(self = [super initWithCoder:aDecoder]){
        NSLog(@"initWithCoder==%@",self.btn);
        // initWithCoder==(null)
    }
    return self;
}

4. SQLite3

什么是SQLite

如何存储数据到数据库

数据库存储数据的步骤

Navicat 是一套适用于MySQL, SQLite等多个数据库系统地图形化数据库管理、报告以及监控的工具。具有高性能的、商业智能的、强大的备份功能

理论基础:

null : 空

例如t_student可以增加一个id字段作为主键,相当于人的身份证

SQL语言简介

select、insert、update、delete、from、create、where、desc、order、by、group、table、alter、view、index等等

可以用于查询获得表中的数据,关键字select是DQL(也是所有SQL)用得最多的操作,其他DQL常用的关键字有where,order by,group by和having

DDL语句

 格式
 create table 表名 (字段名1 字段类型1, 字段名2 字段类型2, …) ;
 示例

 create table t_student (id integer, name text, age inetger, score real) ;

 

 经验

 实际上SQLite是无类型的

 就算声明为integer类型,还是能存储字符串文本(主键除外)

 

 建表时声明啥类型或者不声明类型都可以,也就意味着创表语句可以这么写:

 

 create table t_student(name, age);

 

 为了保持良好的编程规范、方便程序员之间的交流,编写建表语句的时候最好加上每个字段的具体类型

 

 语句优化

 

 创建表格时, 最好加个表格是否已经存在的判断, 这个防止语句多次执行时发生错误

 

 create table if not exists 表名 (字段名1 字段类型1, 字段名2 字段类型2, …) ;

格式

 

 drop table 表名 ;

 

 drop table if exists 表名 ;

 

 示例

 

 drop table t_student ;

 

 语句优化

 

 删除表格时, 最好加个表格是否已经存在的判断, 这个防止语句多次执行时发生错误

 

 drop table if  exists 表名 ;
注意: sqlite里面只能实现Alter Table的部分功能

 

 不能删除一列, 修改一个已经存在的列名

 

 修改表名

 

 ALTER TABLE 旧表名 RENAME TO 新表名

 

 新增属性

 

 ALTER TABLE 表名 ADD COLUMN 列名 数据类型 限定符

不能为空,not null :规定字段的值不能为null
不能重复,unique :规定字段的值必须唯一
默认值,default :指定字段的默认值

示例
`create table t_student (id integer, name text not null unique, age integer not null default 1) ;`

name字段不能为null,并且唯一
age字段不能为null,并且默认为1

添加主键约束的原因?
如果t_student表中就name和age两个字段,而且有些记录的name和age字段的值都一样时,那么就没法区分这些数据,造成数据库的记录不唯一,这样就不方便管理数据,良好的数据库编程规范应该要保证每条记录的唯一性,为此,增加了主键约束,也就是说,每张表都必须有一个主键,用来标识记录的唯一性
- 主键的声明?

在创表的时候用primary key声明一个主键

create table t_student (id integer primary key, name text, age integer) ;

integer类型的id作为t_student表的主键
- 主键字段

只要声明为primary key,就说明是一个主键字段

主键字段默认就包含了not null 和 unique 两个约束

如果想要让主键自动增长(必须是integer类型),应该增加autoincrement

create table t_student (id integer primary key autoincrement, name text, age integer) ;

DML语句

 格式

 

 insert into 表名 (字段1, 字段2, …) values (字段1的值, 字段2的值, …) ;

 

 示例

 

 insert into t_student (name, age) values (‘sz’, 10) ;

 

 注意

 

 数据库中的字符串内容应该用单引号 ’ 括住
格式

 

 update 表名 set 字段1 = 字段1的值, 字段2 = 字段2的值, … ;

 

 示例

 

 update t_student set name = ‘wex’, age = 20 ;

 

 注意

 

 上面的示例会将t_student表中所有记录的name都改为wex,age都改为20
格式

 

 delete from 表名 ;

 

 示例

 

 delete from t_student ;

 

 注意

 

 上面的示例会将t_student表中所有记录都删掉

作用

 

 如果只想更新或者删除某些固定的记录,那就必须在DML语句后加上一些条件

 

 条件语句的常见格式

 

 where 字段 = 某个值 ;   // 不能用两个 =

 

 where 字段 is 某个值 ;   // is 相当于 =

 

 where 字段 != 某个值 ;

 

 where 字段 is not 某个值 ;   // is not 相当于 !=

 

 where 字段 > 某个值 ;

 

 where 字段1 = 某个值 and 字段2 > 某个值 ;  // and相当于C语言中的 &&

 

 where 字段1 = 某个值 or 字段2 = 某个值 ;  //  or 相当于C语言中的 ||

将t_student表中年龄大于10 并且 姓名不等于wex的记录,年龄都改为 5

删除t_student表中年龄小于等于10 或者 年龄大于30的记录

猜猜下面语句的作用

update t_student set score = age where name = ‘wex’ ;

DQL

格式

 

 select 字段1, 字段2, … from 表名 ;

 

 select * from 表名;   //  查询所有的字段

 

 示例

 

 select name, age from t_student ;

 

 select * from t_student ;

 

 select * from t_student where age > 10 ;  //  条件查询

查询相关语句

统计

 

 count(X)

 

 select count(*) from t_student

 

 select count(age) from t_student

 

 avg(X)

 

 计算某个字段的平均值

 

 sum(X)

 

 计算某个字段的总和

 

 max(X)

 

 计算某个字段的最大值

 

 min(X)

 

 计算某个字段的最小值

 

 排序

 

 查询出来的结果可以用order by进行排序

 

 select 字段1, 字段2 from 表名 order by 字段 ;

 

 select * from t_student order by age ;

 

 默认是按照升序排序(由小到大),也可以变为降序(由大到小)

 

 select * from t_student order by age desc ;  //降序

 

 select * from t_student order by age asc ;   // 升序(默认)

 

 也可以用多个字段进行排序

 

 select * from t_student order by age asc, height desc ;

 

 先按照年龄排序(升序),年龄相等就按照身高排序(降序)

 

 limit分页

 

 使用limit可以精确地控制查询结果的数量,比如每次只查询10条数据

 

 格式

 

 select * from 表名 limit 数值1, 数值2 ;

 

 示例

 

 select * from t_student limit 4, 8 ;

 

 可以理解为:跳过最前面4条语句,然后取8条记录

 

 分页

 

 limit常用来做分页查询,比如每页固定显示5条数据,那么应该这样取数据

 

 第1页:limit 0, 5

 

 第2页:limit 5, 5

 

 第3页:limit 10, 5

 

 第n页:limit 5*(n-1), 5

 

 特殊案例

 

 select * from t_student limit 7 ;

 

 相当于select * from t_student limit 0, 7 ;

 

 表示取最前面的7条记录

多表查询


select 字段1, 字段2, … from 表名1, 表名2 ;

 

 别名

 

 select

 

 别名1.字段1 as 字段别名1,

 

 别名2.字段2 as 字段别名2,

 

 …

 

 from

 

 表名1 as 别名1,

 

 表名2 as 别名2 ;

 

 可以给表或者字段单独起别名

 

 as 可以省略

 

 表连接查询

 

 select 字段1, 字段2, … from 表名1, 表名2  where 表名1.id = 表名2.id;

 

 外键

 

 如果表A的主关键字是表B中的字段,则该字段称为表B的外键

 

 保持数据一致性,完整性,主要目的是控制存储在外键表中的数据。 使两张表形成关联,外键只能引用外表中的列的值或使用空值。

 

12-(掌握)代码实现SQLite-DDL

 

 1. 创建一个Swift项目

 

 2. 导入系统框架sqlite3.tbd(sqlite3.dylib)

 

 3. 建立桥接文件, 导入头文件sqlite3.h

 

 1. 新建一个.h 头文件

 

 2. 设置为桥接文件

代码实现


1. 打开数据库

 

 2. 使用打开的数据库, 执行DDL语句, 创建一个数据库表

 

 3.  使用打开的数据库, 执行DDL语句, 创建一个数据库表

 

 3. 将数据库操作封装成一个工具类

 

13-(掌握)代码实现DML语句-Insert

 

 1. 创建一个Student类

 

 属性

 

 name

 

 age

 

 构造方法

 

 init(name: String, age: Int)

 

 2. 创建数据库操作方法

 

 数据库中, 对Student对象的操作封装

 

 insertStudent()

 

14-(了解)代码实现DML语句-Insert绑定参数

 

 准备语句(prepared statement)对象

 

 准备语句(prepared statement)对象一个代表一个简单SQL语句对象的实例,这个对象通常被称为“准备语句”或者“编译好的SQL语句”或者就直接称为“语句”。

 

 操作历程

 

 1. 使用sqlite3_prepare_v2或相关的函数创建这个对象

 

 如果执行成功,则返回SQLITE_OK,否则返回一个错误码

 

 2. 使用sqlite3_bind_*()给宿主参数(host parameters)绑定值

 

 sqlite3_bind_text

 

 参数1:

 

 准备语句

 

 参数2:

 

 绑定的参数索引  (从1开始)

 

 参数3:

 

 绑定的参数内容

 

 参数4:

 

 绑定的参数长度  (-1代表自动计算长度)

 

 参数5:

 

 参数的处理方式

 

 SQLITE_TRANSIENT 会对字符串做一个 copy,SQLite 选择合适的机会释放

 

 SQLITE_STATIC / nil  把它当做全局静态变量, 不会字符串做任何处理,如果字符串被释放,保存到数据库的内容可能不正确!

 

 注意: swift中没有宏的概念

 

 // 替换 sqlite3.h 中的宏

 

 private let SQLITE_TRANSIENT = unsafeBitCast(-1, sqlite3_destructor_type.self)

 

 3.  通过调用sqlite3_step() 一次或多次来执行这个sql

 

 对于DML语句, 如果执行成功, 返回SQLITE_DONE

 

 对于DQL语句, 通过多次执行获取结果集, 继续执行的条件是返回值 SQLITE_ROW

 

 4.  使用sqlite3_reset()重置这个语句,然后回到第2步,这个过程做0次或多次

 

 5. 使用sqlite3_finalize()销毁这个对象, 防止内存泄露

DML语句-Insert插入数据优化

只要在执行多个SQL语句之前, 手动开启事务, 在执行完毕之后, 手动提交事务, 这样 再调用SQL方法执行语句时, 就不会再自动开启和提交事务

事务

5. Core Data

钥匙串

上一篇 下一篇

猜你喜欢

热点阅读