iOS数据持久化(二)
来源:互联网 发布:深圳网络运营商 编辑:程序博客网 时间:2024/05/14 02:08
(写动画库有点烦,等我写好了,会放到git上,到时候放网址。)
SQLite是嵌入式的和轻量级的sql数据库。广泛用于包括浏览器、ios、android以及一些便携需求的小型web应用系统。
但是它也是关系型数据库,所以在功能上是要强于coreData的。
一、离线缓存
在项目开发中,通常都需要对数据进行离线缓存的处理,就像我上一篇说的,新闻类就很需要这样做。
离线缓存一般都是把数据保存到项目的沙盒中。有以下几种方式
(1)归档:NSCodeing、NSKeyedArchiver(我上一篇说过了)
(2)偏好设置:NSUserDefaults
(3)Plist存储:writeToFile
所以,这种情况下,上面的三种就没法应付了。
好了 想用它 自然先导库
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线程安全问题。
这里我就不搬别人的博文了,你们自己去看
好吧,就这些了,啥不懂得 可以问我。
- iOS数据持久化(二)
- iOS 数据持久化(二)
- IOS 数据持久化
- iOS 数据持久化
- ios数据持久化
- IOS数据持久化
- iOS数据持久化
- ios数据持久化
- iOS数据持久化
- iOS 数据持久化
- iOS 数据持久化
- iOS数据持久化
- iOS数据持久化
- iOS数据持久化
- iOS数据持久化
- iOS 数据持久化
- iOS 数据持久化
- ios数据持久化
- iOS之小心使用Storyboard
- python的程序格式要求
- 开关电源拓扑结构详解
- iOS UITableView 移除单元格选中时的高亮状态
- C# 随机字符串
- iOS数据持久化(二)
- chain.doFilter(request,response)含义
- oracle ora-各种常见java.sql.SQLException归纳
- android studio 占用太多内存
- Apache与Nginx优缺点比较
- 经典SQL语句大全
- 入门级--《见缝插针》游戏开发
- 动态类型
- 常用的正则表达式