iOS开发-------Sqlite3实现本地存储简易通讯录

        最近几天整了下Sqlite3,也就是iOS的另外一种储存方式,那么coreData是有什么不足么,不是,一般数据比较简易的时候是不会用coreData的,反而会用自身的sqlte3来实现本地的存储,这就需要用到了点SQL语句了,一般都会用第三方FMDB(第三方库)来简化使用,但第三方的应用是下一次博客的事情了,这次用自带的sqlite3来实现一个简易的通讯录,能够实现保存,并完成增删改查即可,毕竟是最基础的嘛,用第三方虽然简单,但是底层的基础还是要回的。首先看一下如何导入自身的sqlite3的库吧




       如果细看这个图,那么也就知道,这个demo是按照MVC模式写的,既然是MVC模式写的,那么必然就会出现Model(模型类),View(视图类)以及Controller(控制器类)


        这次的页面没有进行细作,只是为了能够了解Sqlite3的用法,首先就是模型类,看看数据库结构





     上面是一个第三方工具,虽然能够快速建立表,但是作为程序员,不建议用它直接建表,建议用sql语句,毕竟还能回顾以及锻炼自己的SQL语句嘛,岂不是一举两得,一般工具是用来查看数据库的。

     

      这里的布局非常简单,只是为了介绍sqlite3的用法,如果想要比较好的视觉可以去之前的博客iOS学习-------简单通讯录(UITableView和CoreData的应用),其他的都是一样的,只是存储的方法变了而已


Model(模型)

这个Model的作用就是存储数据库中表的信息即可,属性如下
//
//  Humen.h
//  Sqilte3
//
//  Created by YueWen on 15/10/6.
//  Copyright (c) 2015年 YueWen. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Humen : NSObject

@property(nonatomic,strong)NSString * name;//姓名

@property(nonatomic,assign)NSInteger age;//年龄

@property(nonatomic,strong)NSString * tele;//电话

@property(nonatomic,strong)NSString * address;//地址

@property(nonatomic,assign)NSInteger humenId;//id


/**
 *  便利初始化方法
 *
 *  @param name    初始name
 *  @param age     初始age
 *  @param tele    初始tele
 *  @param address 初始地址
 *
 *  @return 初始化好的Humen对象
 */
-(instancetype)initWithName:(NSString *)name Age:(NSInteger)age Tele:(NSString *)tele Address:(NSString *)address;

实现便利初始化方法
-(instancetype)initWithName:(NSString *)name Age:(NSInteger)age Tele:(NSString *)tele Address:(NSString *)address
{
    if (self = [super init])
    {
        self.name = name;//初始化name
        self.age = age;//初始化age
        self.tele = tele;//初始化tele
        self.address = address;//初始化address
    }
    
    return self;
}


接下来就是最重点的Sqlite3Manager(数据库管理员),首先他要有增删改查的功能,所以定义四个方法
/**
 *  增加人
 *
 *  @param humen 需要增加的Humen模型
 */
-(void)addHumenToSqlite:(Humen *)humen;

/**
 *  更新数据
 *
 *  @param humen 需要更新的Humen模型
 */
-(void)updateHumenFromSqlite:(Humen *)humen withIndex:(NSInteger)index;


/**
 *  根据下标即id删除
 *
 *  @param index 当前的id
 */
-(void)deleteHumenFromSqlite:(NSInteger)index;


/**
 *  根据姓名查询名字
 *
 *  @param name 查询的名字
 */
-(void)selectHumenFromWithName:(NSString *)name;

其次,Manager一般都是单例,这次也不例外
/**
 *  单例方法
 *
 *  @return 返回单例
 */
+(instancetype)shareSqlite3Manager;


此外,还应该有一个方法能够加载所有的数据,即刷新类似功能的时候,从新从数据库中加载一般数据

/**
 *  加载数据库中所有的数据
 */
-(void)loadMHumen;

如果外界想获得所有的数据,那么必然会有一个对外开放的属性,但是外界是不能更改的,所以是readOnly的属性

/**
 *  存储数据库中的数据数组
 */
@property(nonatomic,strong,readonly)NSArray * humen;


然后是最麻烦的实现方法,先从最简单的东西开始,不要忘记导入自带的sqlite3的头文件

#import <sqlite3.h>

延展中的属性,其中之一必须有导入sqlite3的对象,第二个就是可变数组,用来存储处理好的对象,也就是存储将字典处理好Humen对象,延展如下

@interface Sqlite3Manager ()

@property(nonatomic)sqlite3 * humendate;//不要加strong

@property(nonatomic,strong)NSMutableArray * mHumen;

@end

接着实现重写init方法,以及单例方法,这个相信是再熟悉也不过了

- (instancetype)init
{
    self = [super init];
    if (self) {
        
        //初始化数据数组
        self.mHumen = [NSMutableArray array];
        
        //打开数据库
        [self openSqlite_db];
        
        //创建表
        [self creatSqlite_db];
    
        //加载数据
        [self loadMHumen];
    }
    return self;
}

//单例方法
+(instancetype)shareSqlite3Manager
{
    static Sqlite3Manager * sqliteManager = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        sqliteManager = [[Sqlite3Manager alloc]init];
        
    });
    
    return sqliteManager;
}


再用这个苹果打包好的Sqlite3的时候,实时要注意objc对象和C语言类型的转换,因为SQL是用C语言进行操作的,不认NSString等对象,打开数据库如下

/**
 *  打开数据库
 */
-(void)openSqlite_db
{
    //获取沙盒目录
    NSString * path1 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    
    //追加数据库名称
    NSString * path = [path1 stringByAppendingPathComponent:@"humen.db"];
    
    //打开数据库
    sqlite3_open([path UTF8String],&self->_humendate);//导入的包中的API,因为底层是C语言的东西,所以path需要转成C语言的字符串,后面传一个sqlite3指针的地址

}

接下来就是创建本地的数据库了,用SQL的create table 语句,记得还是C语言的字符串

/**
 *  创建本地的sqlite文件数据库,格式为db
 */
-(void)creatSqlite_db
{
    //创建表的SQL语句,用C语言的字符串,如果用了NSString,记得转成C语言字符串即可
    char * createTableSQL = "create table if not exists t_humen (id integer primary key,name varchar(30),age integer,tele varchar(100),address varchar(100))";
    
    //执行,第一个参数是sqlite3对象指针,第二个是C语言类型的SQL语句,后面3个参数填NULL即可
    sqlite3_exec(self->_humendate, createTableSQL, NULL, NULL, NULL);
    
}

加载数据是什么,就是select * table 语句,将所有的数据库的中的数据全部加载出来,并进行模型的转化

/**
 *  加载数据
 */
-(void)loadMHumen
{
    //清空之前的所有的数据
    [self.mHumen removeAllObjects];
    
    //访问数据库获取数据,根据id,默认为升序
    char * selectSQL = "select * from t_humen order by id";
    
    //创建sqlite3_stmt对象
    sqlite3_stmt * stmt;

    //准备加载
    /**
     *  参数的意义:
     *  1:sqlite3的属性指针
     *  2:执行的sql语句
     *  3: 这里填的是搜说的字节数,-1表示全部
     *  4: 表示填入一个sqlite3_stmt的对象
     */
    sqlite3_prepare_v2(self->_humendate, selectSQL, -1, &stmt, NULL);
    
    //循环加载
    while (sqlite3_step(stmt) ==SQLITE_ROW)
    {
        //初始化一个Humen对象
        Humen * humen = [[Humen alloc] initWithName:[NSString stringWithUTF8String:(char *)(sqlite3_column_text(stmt, 1))]//第一列是名字
                                                Age:sqlite3_column_int(stmt, 2)//第二列是年龄
                                               Tele:[NSString stringWithUTF8String:(char *)(sqlite3_column_text(stmt, 3))]//第三列是电话
                                            Address:[NSString stringWithUTF8String:(char *)(sqlite3_column_text(stmt, 4))]];//第四列是地址
        
        humen.humenId = sqlite3_column_int(stmt, 0);
        //添加一个数组
        [self.mHumen addObject:humen];
    }
    
    sqlite3_finalize(stmt);//表示完成,回馈一下
}



    如何完成增删改查呢,这里需要了解一个机制叫做预编译,如果不用预编译,那么输入的某些SQL语句中的特殊字符,比如#等,会造成一些不必要而且还很难解决的问题,最麻烦的就是数据库的数据的安全会很危险,所以下面的方法都是用的自带的预编译语句进行操作

首先是增加人,如何增加,直接传入一个Humen对象,其他的操作在方法里操作即可,用SQL语句中的insert into

/**
 *  增加人
 *
 *  @param humen 需要增加的Humen模型
 */
-(void)addHumenToSqlite:(Humen *)humen
{
    //为数组添加数据
    [self.mHumen addObject:humen];
    
    //预编译语句
    char * insertSQL = "insert into t_humen values(NULL,?,?,?,?)";
    
    sqlite3_stmt * stmt;

    //准备
    sqlite3_prepare_v2(self->_humendate, insertSQL, -1, &stmt, NULL);
    
    //为stmt预编译语句绑定数据,从1开始
    sqlite3_bind_text(stmt, 1, [humen.name UTF8String], -1, NULL);//为name绑定
    sqlite3_bind_int(stmt, 2, (int)humen.age);//为age绑定
    sqlite3_bind_text(stmt, 3, [humen.tele UTF8String], -1, NULL);//为tele绑定
    sqlite3_bind_text(stmt, 4, [humen.address UTF8String], -1, NULL);//为address绑定

    //单步调试
    int rst = sqlite3_step(stmt);
    
    if (rst == SQLITE_DONE)
    {
        NSLog(@"插入成功!");
    }else
    {
        NSLog(@"失败!编号为:%d",rst);
    }
    
    sqlite3_finalize(stmt);
}
  
      删除对象,即需要知道数据库中属性的主键,在创建表的时候,primary key 表示主键,即 不能重复,就像人的身份证一样,唯一标识符,所以必须传入一个主键的数字,这个数字就是对象中的humen_id属性,因为加载的顺序就是按照主键顺序的

/**
 *  删除指定id的人对象
 *
 *  @param index 下标 + 1
 */
-(void)deleteHumenFromSqlite:(NSInteger)index
{
    //获取当前的对象id
    NSInteger ID = ((Humen *)self.mHumen[index - 1]).humenId;
    
    [self.mHumen removeObjectAtIndex:index - 1];

    //从数据库中删除
    //预编译语句
    char * deleteSQL = "delete from t_humen where id=?";
    
    sqlite3_stmt * stmt;
    
    //准备
    sqlite3_prepare_v2(self.humendate, deleteSQL, -1, &stmt, NULL);
    
    //绑定数据
    sqlite3_bind_int(stmt, 1, (int)ID);
    
    //分布调试
    int rst = sqlite3_step(stmt);
    
    //如果完成
    if (rst == SQLITE_DONE)
    {
        NSLog(@"删除成功!");
    }
    
    else
    {
        NSLog(@"删除失败!");
    }
    
    sqlite3_finalize(stmt);
}



更新对象,需要知道一个修改后的对象以及修改对象的id方便在数据库中进行修改,用update table语句即可

/**
 *  更新数据
 *
 *  @param humen 需要更新的Humen模型
 */
-(void)updateHumenFromSqlite:(Humen *)humen withIndex:(NSInteger)index
{
    //获取当前的对象的ID
    NSInteger ID = ((Humen *)self.humen[index - 1]).humenId;
   
    //数组替换
    [self.mHumen replaceObjectAtIndex:index - 1 withObject:humen];
  
    //预编译语句
    char * updateSQL = "update t_humen set name=?,age=?,tele=?,address=? where id=?";
    
    sqlite3_stmt * stmt;
    
    //准备
    sqlite3_prepare_v2(self.humendate, updateSQL, -1, &stmt, NULL);
    
    //绑定参数数据
    sqlite3_bind_text(stmt, 1, [humen.name UTF8String], -1, NULL);
    sqlite3_bind_int(stmt, 2, (int)humen.age);
    sqlite3_bind_text(stmt, 3, [humen.tele UTF8String], -1, NULL);
    sqlite3_bind_text(stmt, 4, [humen.address UTF8String], -1, NULL);
    sqlite3_bind_int(stmt, 5, (int)ID);
    
    //分部调试
    int rst = sqlite3_step(stmt);
    
    //如果更新完成
    if (rst == SQLITE_DONE)
    {
        NSLog(@"更新成功!");
    }else
    {
        NSLog(@"失败!%d",rst);
    }
    
    sqlite3_finalize(stmt);
}



      最后一个功能就是查,查的话在这个方案里是不需要再对数据库进行操作的,因为增删改的时候对本地的数组进行了操作,所以本地的数组存的数据就是实时的数据库里存的对象,整个工程只需要在初始化的时候对数据库进行一次加载即可,查询如下

/**
 *  根据姓名查询名字
 *
 *  @param name 查询的名字
 */
-(void)selectHumenFromWithName:(NSString *)name
{
    //可变数组存储符合条件的对象
    NSMutableArray * mutableHumen = [NSMutableArray array];
    
    //只需在数组中查询即可,不需要在执行数据库
    for (Humen * humen in self.mHumen)
    {
        if ([humen.name isEqualToString:name])
        {
            //添加到数组
            [mutableHumen addObject:humen];
        }
    }
    
    //改变当前的数组
    self.mHumen = mutableHumen;
}

     既然打开了数据库,那么必然存在关闭数据库,这个demo的思路是在创建的时候打开数据库,直到程序结束的时候才会关闭,所以必然有一个关闭数据库的方法

/**
 *  关闭数据库
 */
-(void)closeSqlite_db
{
    sqlite3_close(self->_humendate);
}


重写dealloc中的方法,说到dealloc中的方法,在ARC环境下,是不需要写[super dealloc]的,写上也会报错

/**
 *  消除对象的时候关闭数据库
 */
-(void)dealloc
{
    //关闭数据库
    [self closeSqlite_db];
}


由此,Sqlite3Manager就完成了


View(视图)


页面的布局依旧选择了storyboard,简单布局如下,root视图控制器为一个导航控制器,连接着能够显示的cell,后面的页面负责添加以及更新



Controller(控制器)

首先创建一个显示信息的主页面,为一个tableViewController,取名为:HumenTableViewController
延展中只需定义一个sqlite3Manager即可
@interface HumenTableViewController ()

@property(nonatomic,strong)Sqlite3Manager * sqliteManager;

@end

加载viewDidLoad
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //初始化单例
    self.sqliteManager = [Sqlite3Manager shareSqlite3Manager];
    
    //添加导航栏的右按钮,回调方法为 toAddViewController
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(toAddViewController)];
    
    //添加导航栏的左按钮,回调方法为 toSearchViewController
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(toSearchViewController)];
}

这里需要一个方法,叫做viewWillApear,比如在当前页面即将展现的时候触发的,需要刷新一下列表,比如更新,添加页面,调回的时候,需要刷新一下列表
//页面将要跳回动画结束时
-(void)viewWillAppear:(BOOL)animated
{
    //重新加载数据库中的所有数据
    [self.sqliteManager loadMHumen];
    
    //刷新列表
    [self.tableView reloadData];
}

为添加按钮设置回调方法,只负责跳转一个页面即可
#pragma mark - barButton target action
-(void)toAddViewController
{
    //获取storyboard
    UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
    
    //根据storyboard创建控制对象
    UIViewController * viewController = [storyboard instantiateViewControllerWithIdentifier:@"AddViewController"];
    
    //跳转界面
    [self.navigationController pushViewController:viewController animated:YES];
}

为搜索按钮设置一个回调方法,这里不再进行页面跳转,只需弹出一个alertController即可,详细可以去之前的博客 iOS学习-------UIAlertController(弹出视图控制器)
-(void)toSearchViewController
{
    //准备alertController
    UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"搜索" message:@"请填入搜索的名字" preferredStyle:UIAlertControllerStyleAlert];
    
    //添加文本框
    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
    }];
    
    //添加搜索的动作按钮
    [alert addAction:[UIAlertAction actionWithTitle:@"搜索" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
        //获取搜索的名字
        NSString * name = alert.textFields[0].text;
        
        //在数据库对象中进行搜索
        [self.sqliteManager selectHumenFromWithName:name];
        
        //刷新列表
        [self.tableView reloadData];
        
        //跳回
        [self dismissViewControllerAnimated:YES completion:nil];
        
    }]];
    
    //添加取消的动作按钮
    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        
        //跳回
        [self dismissViewControllerAnimated:YES completion:nil];
    }]];
    
    //跳出
    [self presentViewController:alert animated:YES completion:nil];
    
}

效果如下:




接下来是最为熟悉的三个数据源代理方法,不做赘余
#pragma mark - Table view data source
/**
 *  返回组数,分组的情况下,因为不分组,所以返回1
 *
 *  @param tableView 当前的tableView
 *
 *  @return 组数
 */
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;
}

/**
 *  组的行数
 *
 *  @param tableView 当前的tableView
 *  @param section   组的个数
 *
 *  @return 返回每部分的行数
 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return self.sqliteManager.humen.count;

}


/**
 *  cell显示的内容
 *
 *  @param tableView 当前的tableView
 *  @param indexPath 当前的位置
 *
 *  @return 打包好的cell
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    //获取模型
    Humen * humen = self.sqliteManager.humen[indexPath.row];
    
    //创建表格cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    
    //简单赋值
    cell.textLabel.text = humen.name;
    
    return cell;
}

因为修改界面是通过点击当前的行来跳入的,因为需要实现如下代理方法
/**
 *  点击行的时候
 *
 *  @param tableView 当前的tableView
 *  @param indexPath 当前的位置
 */
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //获取当前的模型
    Humen * humen = self.sqliteManager.humen[indexPath.row];
    
    //获得当前的storyboard
    UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
    
    //获得跳转的ViewController
    UIViewController * vc = [storyboard instantiateViewControllerWithIdentifier:@"AddViewController"];
    
    //KVC赋值
    [vc setValue:humen forKey:@"humen"];
    [vc setValue:indexPath forKey:@"index"];
    
    //跳转
    [self.navigationController pushViewController:vc animated:YES];
    
}

增删改查已经完成了增,改,查的页面,删除如何呢,想点击按住一拖进行删除来完成,那么需要完成两个代理方法

首先将可以编辑设置为YES,默认为NO
/**
 *  列表视图能够编辑
 *
 *  @param tableView 当前的tableView
 *  @param indexPath 当前的位置
 *
 *  @return YES表示可以编辑  NO表示不可以编辑
 */
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    
    return YES;
}

接着实现编辑模式下的不同编辑风格进行的操作,因为只完成删除,因此只完成了删除操作
/**
 *  当前的编辑模式进行相应的操作
 *
 *  @param tableView    当前的tableView
 *  @param editingStyle 编辑的风格
 *  @param indexPath    当前的位置
 */
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        
        //从数据库中删除当前的对象
        [self.sqliteManager deleteHumenFromSqlite:indexPath.row + 1];
        
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }   
}

删除展示图如下




      接着完成添加用户以及更新用户页面,因为页面相似,所以不想再做两个相同页面,只需一个属性,即Humen,判断依据是,只有更新的时候他才会有值,添加的时候是nil,延展如下
#import "AddViewController.h"
#import "Sqlite3Manager.h"
#import "Humen.h"

@interface AddViewController ()

@property (strong, nonatomic) IBOutlet UITextField *nameText;
@property (strong, nonatomic) IBOutlet UITextField *ageText;
@property (strong, nonatomic) IBOutlet UITextField *teleText;
@property (strong, nonatomic) IBOutlet UITextField *addressText;

@property (nonatomic,strong) Sqlite3Manager * sqliteManager;

//更新时专用
@property(strong,nonatomic)Humen * humen;
@property(nonatomic,strong)NSIndexPath * index;

@end

实现viewDidLoad,根据属性中的human的有无来判断进入何种页面
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //实例化单例属性
    self.sqliteManager = [Sqlite3Manager shareSqlite3Manager];

    //设置导航的右上角按钮
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(addHumenToSqliteFromAddViewController)];
    
    //表示代表更新视图
    if (self.humen)
    {
        self.navigationItem.title = @"更新视图";
        //赋值
        self.nameText.text = self.humen.name;
        self.ageText.text = [NSString stringWithFormat:@"%ld",self.humen.age];
        self.teleText.text = self.humen.tele;
        self.addressText.text = self.humen.address;
    }
    
    else
    {
        self.navigationItem.title = @"添加视图";
    }
}

点击完成的时候,也是需要判断是添加还是更新的,毕竟SQL语句是不一样的,因此,回调方法如下
-(void)addHumenToSqliteFromAddViewController
{
    
    //初始化humen实例
    Humen * humen = [[Humen alloc]initWithName:self.nameText.text Age:[self.ageText.text intValue] Tele:self.teleText.text Address:self.addressText.text];
    
    //表示更新
    if (self.humen)
    {
        //更新到数据库
        [self.sqliteManager updateHumenFromSqlite:humen withIndex:self.index.row + 1];
    }
    
    //表示添加
    else
    {
        //添加到数据库sqlite
        [self.sqliteManager addHumenToSqlite:humen];
    }

    //调回原页面
    [self.navigationController popViewControllerAnimated:YES];
    
}

这样基本的增删改查就已经完成了,看一下效果吧

增加效果:

更新效果:



查找效果:


删除效果:


再次打开模拟器,还是可以保存的,如下

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值