在IOS开发中遇到数据库保存很正常。但是要保存的对象中如果不全是基本数据类型,或者不是数据库是支持的类型,是不是瞬间石化了。
例如:
在一个Message对象中包含一个Attatchment对象,现在要求是每个Message要保存起来,选择sqlite数据库保存是不是很正常(反正我是用的他),但是sqlite中并不支持Attatchment类型,怎么搞???
法一:把Attatchment中的所有属性放到Message中,没有问题,也是一个很好的办法,但是这样是不是有点牵强呢?
法二:也是本人今天要介绍的方法,就是对象归档成NSData后在存储。查询出来的时候在解档一下就OK了。废话少说,直接给出代码(数据库存储用到了第三方库FMDB):
给出Attatchment实体类(只是做一个简单的测试就不给出详细定义了):
// // Attatchment.h // Demo // #import <Foundation/Foundation.h> @interface Attatchment : NSObject <NSCoding>{ NSString *_localPath; } @property (nonatomic , copy) NSString *localPath; @end // // Attatchment.m // Demo // #import "Attatchment.h" @implementation Attatchment @synthesize localPath = _localPath; - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:_localPath forKey:@"localPath"]; } - (id)initWithCoder:(NSCoder *)aDecoder{ self = [super init]; if(self != nil){ _localPath = [[aDecoder decodeObjectForKey:@"localPath"] retain]; } return self; } @end
在给出Message实体(也是一个简单的定义):
// // Message.h // Demo // #import <Foundation/Foundation.h> #import "Attatchment.h" @interface Message : NSObject{ NSString *_messageId; Attatchment *_att; } @property (nonatomic , copy) NSString *messageId; @property (nonatomic , retain) Attatchment *att; @end // // Message.m // Demo // #import "Message.h" @implementation Message @synthesize messageId = _messageId; @synthesize att = _att; @end
这样两个实体就定义完了。
重要的过程就是存储了:
这里给出了自己写的一个DBManager(写的粗糙,见谅!)
// // DBManager.h // FetionHD // #import <Foundation/Foundation.h> #import "FMDatabase.h" #import "Message.h" #define dataBasePath [[(NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)) lastObject]stringByAppendingPathComponent:dataBaseName] #define dataBaseName @"dataBase.sqlite" @interface DBManager : NSObject /****/ /** * @brief 数据库对象单例方法 * * @return 返回FMDateBase数据库操作对象 */ + (FMDatabase *)createDataBase; /** * @brief 关闭数据库 */ + (void)closeDataBase; /** * @brief 清空数据库内容 */ + (void)deleteDataBase; /** * @brief 判断表是否存在 * * @param tableName 表明 * * @return 创建是否成功 */ + (BOOL) isTableExist:(NSString *)tableName; /** * @brief 创建所有表 * * @return */ + (BOOL)createTable; /** * @brief 添加chatdata 如果主键重复就更新 * * @param chatData 要保存的chatdata * * @return 返回是否保存或者更新成功 */ + (BOOL) saveOrUpdataMessage:(Message*)chatData; + (Message *) selectMessageByMessageId:(NSString*)messageId; @end // // DBManager.m // FetionHD #import <Foundation/Foundation.h> #import "DBManager.h" #define debugMethod(...) NSLog((@"In %s,%s [Line %d] "), __PRETTY_FUNCTION__,__FILE__,__LINE__,##__VA_ARGS__) static FMDatabase *shareDataBase = nil; @implementation DBManager /** 创建数据库类的单例对象 **/ //+ (FMDatabase *)createDataBase { // //debugMethod(); // @synchronized (self) { // if (shareDataBase == nil) { // // shareDataBase = [[FMDatabase databaseWithPath:dataBasePath] retain]; // } // return shareDataBase; // } //} //这种方法可以达到线程安全,但多次调用时会导致性能显著下降 + (FMDatabase *)createDataBase { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shareDataBase = [[FMDatabase databaseWithPath:dataBasePath] retain]; }); return shareDataBase; } /** 判断数据库中表是否存在 **/ + (BOOL) isTableExist:(NSString *)tableName { FMResultSet *rs = [shareDataBase executeQuery:@"select count(*) as 'count' from sqlite_master where type ='table' and name = ?", tableName]; while ([rs next]) { // just print out what we've got in a number of formats. NSInteger count = [rs intForColumn:@"count"]; NSLog(@"%@ isOK %d", tableName,count); if (0 == count) { return NO; } else { return YES; } } return NO; } /** 创建表 **/ + (BOOL)createTable { debugMethod(); NSLog(@"%@",dataBasePath); if (1){ { shareDataBase = [DBManager createDataBase]; if ([shareDataBase open]) { if (![DBManager isTableExist:@"message_table"]) { NSString *sql = @"CREATE TABLE \"message_table\" (\"message_id\" TEXT PRIMARY KEY NOT NULL check(typeof(\"message_id\") = 'text') , \"att\" BLOB)"; NSLog(@"no Medicine "); [shareDataBase executeUpdate:sql]; } [shareDataBase close]; } } } return YES; } /** 关闭数据库 **/ + (void)closeDataBase { if(![shareDataBase close]) { NSLog(@"数据库关闭异常,请检查"); return; } } /** 删除数据库 **/ + (void)deleteDataBase { if (shareDataBase != nil) { //这里进行数据库表的删除工作 } } + (BOOL) saveOrUpdataMessage:(Message*)message { BOOL isOk = NO; shareDataBase = [DBManager createDataBase]; if ([shareDataBase open]) { isOk = [shareDataBase executeUpdate: @"INSERT INTO \"message_table\" (\"message_id\",\"att\") VALUES(?,?)",message.messageId,[NSKeyedArchiver archivedDataWithRootObject:message.att]]; [shareDataBase close]; } return isOk; } + (Message *) selectMessageByMessageId:(NSString*)messageId { Message *m = nil; shareDataBase = [DBManager createDataBase]; if ([shareDataBase open]) { FMResultSet *s = [shareDataBase executeQuery:[NSString stringWithFormat:@"SELECT * FROM \"message_table\" WHERE \"message_id\" = '%@'",messageId]]; if ([s next]) { m = [[Message alloc] init]; m.messageId = [s stringForColumn:@"message_id"]; m.att = [NSKeyedUnarchiver unarchiveObjectWithData:[s dataForColumn:@"att"]]; } [shareDataBase close]; } return m; } @end
剩下的工作就是搞个界面调用一下
- (IBAction)addAction:(id)sender{ Message *message = [[Message alloc] init]; Attatchment *att = [[Attatchment alloc] init]; message.messageId = @"11"; message.att = att; att.localPath = @"a.png"; [DBManager createTable]; [DBManager saveOrUpdataMessage:message]; [att release]; [message release]; } - (IBAction)selectAction:(id)sender{ Message *message = [DBManager selectMessageByMessageId:@"11"]; NSLog(@"%@",message.att.localPath); NSLog(@"%@",message.messageId); }
记得在工程中添加FMDB库和sqlite3库,跑一下试试会有想不到的效果哦。