iOS数据持久化(二)

来源:互联网 发布:深圳网络运营商 编辑:程序博客网 时间:2024/05/14 02:08

(写动画库有点烦,等我写好了,会放到git上,到时候放网址。)

SQLite是嵌入式的和轻量级的sql数据库。广泛用于包括浏览器、ios、android以及一些便携需求的小型web应用系统。

但是它也是关系型数据库,所以在功能上是要强于coreData的。

一、离线缓存

在项目开发中,通常都需要对数据进行离线缓存的处理,就像我上一篇说的,新闻类就很需要这样做。

离线缓存一般都是把数据保存到项目的沙盒中。有以下几种方式

(1)归档:NSCodeing、NSKeyedArchiver(我上一篇说过了)

(2)偏好设置:NSUserDefaults

(3)Plist存储:writeToFile

但是这三者中后两者只能存小型数据,而第一种其实也是不适合大量的数据的,主要是它的存取机制,想存先取,打个比方,你归档了200个模型数据,想增加一个,就必须要先全部读取出来,加上去之后在全部存入。这种很费资源。

所以,这种情况下,上面的三种就没法应付了。

好了  想用它 自然先导库


libsqlite3.dylib, 3是第三个版本的意思。推荐一个博文你们阅读下

由于原生是C语言格式,所以我们先讲原生,然后给大家讲个三方库,很强大,开发时经常用到。

先引用库。#import "sqlite3.h"


然后创建数据库,如果没有就创建(sqlite3_open):

为了避免重复创建,所以在创建前先判断,不过说到这里我想起来了,强调一下,之前一直说 SQLite3不是线程安全,所以所以 一般情况都是加上锁。但是最近我看了一篇博文,之后发现其实是是支持的。等文章最后再说。


<span style="font-size:18px;">sqlite3* database_;-(BOOL) open{    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);    NSString *documentsDirectory = [paths objectAtIndex:0];    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"mydb.sql"];    NSFileManager *fileManager = [NSFileManager defaultManager];    BOOL find = [fileManager fileExistsAtPath:path];    //找到数据库文件mydb.sql    if (find) {        NSLog(@"Database file have already existed.");        if(sqlite3_open([path UTF8String], &database_) != SQLITE_OK) {            sqlite3_close(database_);            NSLog(@"Error: open database file.");            return NO;        }        return YES;    }    if(sqlite3_open([path UTF8String], &database_) == SQLITE_OK) {        bFirstCreate_ = YES;        [self createChannelsTable:database_];//在后面实现函数createChannelsTable        return YES;    } else {        sqlite3_close(database_);        NSLog(@"Error: open database file.");        return NO;    }    return NO;}</span>


创建表格:

<span style="font-size:18px;">//创建表格,假设有五个字段,(id,cid,title,imageData ,imageLen ) //说明一下,id为表格的主键,必须有。 //cid,和title都是字符串,imageData是二进制数据,imageLen 是该二进制数据的长度。    - (BOOL) createChannelsTable:(sqlite3*)db  {      char *sql = "CREATE TABLE channels (id integer primary key, \                                         cid text, \                                          title text, \                                         imageData BLOB, \                                         imageLen integer)";     sqlite3_stmt *statement;     if(sqlite3_prepare_v2(db, sql, -1, &statement, nil) != SQLITE_OK) {         NSLog(@"Error: failed to prepare statement:create channels table");        return NO;     }     int success = sqlite3_step(statement);     sqlite3_finalize(statement);     if ( success != SQLITE_DONE) {        NSLog(@"Error: failed to dehydrate:CREATE TABLE channels");        return NO;     }    NSLog(@"Create table 'channels' successed.");     return YES; }</span>

插入数据:

<span style="font-size:18px;">- (BOOL) insertOneChannel:(Channel*)channel{    NSData* ImageData = UIImagePNGRepresentation( channel.image_);    NSInteger Imagelen = [ImageData length];    sqlite3_stmt *statement;    static char *sql = "INSERT INTO channels (cid,title,imageData,imageLen)\                        VALUES(?,?,?,?)";    //问号的个数要和(cid,title,imageData,imageLen)里面字段的个数匹配,代表未知的值,将在下面将值和字段关联。    int success = sqlite3_prepare_v2(database_, sql, -1, &statement, NULL);    if (success != SQLITE_OK)     {        NSLog(@"Error: failed to insert:channels");        return NO;    }      //这里的数字1,2,3,4代表第几个问号    sqlite3_bind_text(statement, 1, [channel.id_ UTF8String], -1, SQLITE_TRANSIENT);    sqlite3_bind_text(statement, 2, [channel.title_ UTF8String], -1, SQLITE_TRANSIENT);    sqlite3_bind_blob(statement, 3, [ImageData bytes], Imagelen, SQLITE_TRANSIENT);    sqlite3_bind_int(statement, 4, Imagelen);        success = sqlite3_step(statement);    sqlite3_finalize(statement);        if (success == SQLITE_ERROR) {        NSLog(@"Error: failed to insert into the database with message.");        return NO;    }      NSLog(@"Insert One Channel#############:id = %@",channel.id_);    return YES;}</span>

查询数据:
- (void) getChannels:(NSMutableArray*)fChannels{    sqlite3_stmt *statement = nil;    char *sql = "SELECT * FROM channels";    if (sqlite3_prepare_v2(database_, sql, -1, &statement, NULL) != SQLITE_OK)     {        NSLog(@"Error: failed to prepare statement with message:get channels.");    }    //查询结 果集中一条一条的遍历所有的记录,这里的数字对应的是列值。    while (sqlite3_step(statement) == SQLITE_ROW)     {        char* cid       = (char*)sqlite3_column_text(statement, 1);        char* title     = (char*)sqlite3_column_text(statement, 2);        Byte* imageData = (Byte*)sqlite3_column_blob(statement, 3);        int imageLen    = sqlite3_column_int(statement, 4);                Channel* channel = [[Channel alloc] init];        if(cid)            channel.id_ = [NSString stringWithUTF8String:cid];        if(title)            channel.title_ = [NSString stringWithUTF8String:title];        if(imageData)        {            UIImage* image = [UIImage imageWithData:[NSData dataWithBytes:imageData length:imageLen]];            channel.image_ = image;        }        [fChannels addObject:channel];        [channel release];    }    sqlite3_finalize(statement);}

上面的方法不用说,就是看着不舒服,所以,我给大家推荐个第三方库,FMDB

毕竟第三方库,做的真的很好。FMDB下载

通过FMDB,你可以定制你自己的DBManager,我做了一个简单的,直接上代码,很容易懂。

这是我的.h,自己定的一些接口,完成增删查改的功能。

<span style="font-size:18px;">////  DBManager.h//  FMDBDemo////  Created by JackYang on 15/9/8.//  Copyright (c) 2015年 JackYang. All rights reserved.//#import <Foundation/Foundation.h>#import "PersonModel.h"@interface DBManager : NSObject+(instancetype)sharedInstance;- (void)addPerson:(PersonModel*)model;- (void)deletePerson:(PersonModel*)model;- (void)updatePerson:(PersonModel*)model;- (NSArray*)allPerson;- (BOOL)isPersonExists:(PersonModel*)model;@end</span>

然后是.m的实现,由于我只是写了一个Demo,所以只是做了一个PersonModel,实现就是增加,删除person,你们可以自己创建自己想要的表,定制自己的sql语句,w3school 有sql教程。不会的可以去看看。

<span style="font-size:18px;">////  DBManager.m//  FMDBDemo////  Created by JackYang on 15/9/8.//  Copyright (c) 2015年 JackYang. All rights reserved.//#import "DBManager.h"#import "FMDatabase.h"@interface DBManager ()//定义数据库,FMDatabase 来描述操作数据库@property(nonatomic)FMDatabase *db;@end@implementation DBManager//数据库本质也是一个文件,DBMS 来进行管理+(instancetype)sharedInstance{    static DBManager *s_dbManager = nil;//    //@synchronized(self) 内部实现时加锁,防止多线程不安全//    @synchronized(self){//        if (s_dbManager == nil) {//            s_dbManager = [[DBManager alloc]init];//        }//        return s_dbManager;//    }    //两种方式可以创建单例。但是这是共享单例,你可以选择吧init隐藏,就是换个方法名,不对外公开。    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        s_dbManager = [[DBManager alloc]init];    });    return s_dbManager;}- (id)init{    if (self = [super init]) {        //创建数据库,在数据库中创建表        NSString *dbFilePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Person.db"];        NSLog(@"DB Path = %@",dbFilePath);        //使用数据库文件路径,初始化db实例        self.db = [[FMDatabase alloc]initWithPath:dbFilePath];                //打开数据库,如果数据库文件不存在,FMDB自动创建一个,并打开数据库        if(![self.db open]){            NSLog(@"打开数据库失败");        }else{            [self createPersonTable];        }            }    return self;}- (void)createPersonTable{    NSString *sql = @"CREATE TABLE IF NOT EXISTS Persons(PersonId INTEGER PRIMARY KEY AUTOINCREMENT,Age INTEGER,FirstName TEXT,LastName TEXT,Address TEXT,City varchar(255),HeadImage BLOB)";        //使用FMDB时,对sql语句的操作    //executeQuery 针对select 语句    //[self.db executeQuery:<#(NSString *), ...#>];        //update ,delete,insert 使用executeUpdate    if (![self.db executeUpdate:sql]) {        NSLog(@"创建Person表失败");        [self.db close];    };}- (void)addPerson:(PersonModel*)model{    //?代表是占位符,该位置会填充相应的值,注意:传入的值一定是id类型,如果是整数,浮点数等,需要转成NSNumber    NSString *sql = @"INSERT INTO Persons(Age,FirstName,LastName,Address,City,HeadImage) VALUES(?,?,?,?,?,?)";        //UIImagePNGRepresentation 把图片转成NSData的数据    NSData *imageData = UIImagePNGRepresentation(model.headImage);    BOOL result = [self.db executeUpdate:sql,@(model.age),model.firstName,model.lastName,model.address,model.city,imageData];    if(result == NO){        NSLog(@"插入数据失败");    }}- (void)deletePerson:(PersonModel*)model{    NSString *sql = @"DELETE FROM Persons WHERE PersonId = ?";    BOOL result = [self.db executeUpdate:sql,@(model.personId)];    if(result == NO){        NSLog(@"插入数据失败");    }}- (void)updatePerson:(PersonModel*)model{    NSString *sql = @"UPDATE Persons SET Age = ?,FirstName = ?,LastName = ?,Address = ?,City = ? WHERE PersonId = ?";    BOOL result = [self.db executeUpdate:sql,@(model.age),model.firstName,model.lastName,model.address,model.city,@(model.personId)];    if (!result) {        NSLog(@"更新记录失败");    }}- (NSArray*)allPerson{    NSMutableArray *resultArray = [NSMutableArray array];    NSString *sql = @"SELECT * FROM Persons";    FMResultSet *resultSet = [self.db executeQuery:sql];    //resultSet.next 指向的是一条记录    while (resultSet.next) {        PersonModel *person = [[PersonModel alloc]init];        person.personId   = [resultSet intForColumn:@"personId"];        person.age        = [resultSet intForColumn:@"Age"];        person.firstName  = [resultSet stringForColumn:@"FirstName"];        person.lastName   = [resultSet stringForColumn:@"LastName"];        person.address    = [resultSet stringForColumn:@"Address"];        person.city       = [resultSet stringForColumn:@"City"];        NSData *imageData = [resultSet dataForColumn:@"HeadImage"];        person.headImage  = [UIImage imageWithData:imageData];                [resultArray addObject:person];    }    [resultSet close];    return resultArray;}- (BOOL)isPersonExists:(PersonModel*)model{    BOOL result = NO;    NSString *sql = @"SELECT * FROM Persons WHERE personId = ?";    FMResultSet *resultSet = [self.db executeQuery:sql,@(model.personId)];    if (resultSet.next) {        result = YES;    }    [resultSet close];    return result;}@end</span>

在FMDB中 增删改 都是使用的 executeUpdate,只有 查使用的时executeQuery,这里要注意点

而且,在查的时候 也是和原生一样 使用结果集,进行遍历。

剩下的是就是使用你的工具类,存储你的数据了


<span style="font-size:18px;">- (IBAction)getAllPerson:(id)sender {    self.personList = [[DBManager sharedInstance] allPerson];    [self.tableView reloadData];}- (IBAction)addPerson:(id)sender {    PersonModel *person = [[PersonModel alloc]init];    person.age = 20;    person.firstName = @"123";    person.lastName  = @"456";    person.address   = @"china";    person.city      = @"HeFei";    person.headImage = [UIImage imageNamed:@"colors@2x.gif"];    [[DBManager sharedInstance] addPerson:person];}- (IBAction)deletePerson:(id)sender {    PersonModel *person = [self.personList objectAtIndex:0];    [[DBManager sharedInstance] deletePerson:person];}- (IBAction)updatePerson:(id)sender {    PersonModel *person = [self.personList objectAtIndex:0];    person.firstName = @"aaa";    person.lastName  = @"bbb";        [[DBManager sharedInstance] updatePerson:person];}</span>

好了,还有什么没有说?

没错,就是原生sql线程安全问题。

这里我就不搬别人的博文了,你们自己去看

好吧,就这些了,啥不懂得 可以问我。








0 0
原创粉丝点击