iOS 面向模型的 SQL存储

本地化存储也是app开发当中比较常见的功能需求。比如一些列表界面(tableview)相关的数据存储。

本文就以tableview界面数据的存储为例。

为简单起见,demo中使用了MJExtension及MJRefresh框架,采用常用的MVC代码结构,我们将在此基础上扩展其本地化存储功能。列表界面的核心代码如下(灰色背景的部分表示将要实现和扩展的方法):

 

  1 - (void)setupRefresh {
  2 
  3     self.tableView.mj_header = [XXRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewTopics)];
  4 
  5     [self.tableView.mj_header beginRefreshing];
  6 
  7         self.tableView.mj_footer = [XXRefreshFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreTopics)];
  8 
  9 }
 10 
 11 #pragma mark - 数据加载
 12 
 13 - (void)loadNewTopics {
 14 
 15     // 取消所有请求
 16 
 17     [self.manager.tasks makeObjectsPerformSelector:@selector(cancel)];
 18 
 19     // 参数
 20 
 21     NSMutableDictionary *params = [NSMutableDictionary dictionary];
 22 
 23     params[@"a"] = self.aParam;
 24 
 25     params[@"type"] = @(self.type);
 26 
 27     __weak typeof(self) weakSelf = self;
 28 
 29     // 发送请求
 30 
 31     [self.manager GET:XXCommonURL parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
 32 
 33         // 存储maxtime(方便用来加载下一页数据)
 34 
 35         weakSelf.maxtime = responseObject[@"info"][@"maxtime"];
 36 
 37            // 字典数组 -> 模型数组
 38 
 39         weakSelf.topics = [XXTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];
 40 
 41             // 2. 在本地sql做缓存  todo
 42 
 43        [weakSelf saveToSql: weakSelf.topics ] ;  
 44 
 45               // 刷新表格
 46 
 47         [weakSelf.tableView reloadData];
 48 
 49               // 让[刷新控件]结束刷新
 50 
 51         [weakSelf.tableView.mj_header endRefreshing];
 52 
 53     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
 54 
 55         // 让[刷新控件]结束刷新
 56 
 57         [weakSelf.tableView.mj_header endRefreshing];
 58 
 59         // 加载缓存、本地数据库 todo
 60 
 61            weakSelf.topics = [weakSelf getFromeSql];
 62 
 63           // 刷新表格
 64 
 65         [weakSelf.tableView reloadData];
 66 
 67         }];
 68 
 69 }
 70 
 71  
 72 
 73 - (void)loadMoreTopics {
 74 
 75     // 取消所有的请求
 76 
 77     [self.manager.tasks makeObjectsPerformSelector:@selector(cancel)];
 78 
 79      // 参数
 80 
 81     NSMutableDictionary *params = [NSMutableDictionary dictionary];
 82 
 83     params[@"a"] = self.aParam;
 84 
 85     params[@"maxtime"] = self.maxtime;
 86 
 87     params[@"type"] = @(self.type);
 88 
 89     __weak typeof(self) weakSelf = self;
 90 
 91     // 发送请求
 92 
 93     [self.manager GET:XXCommonURL parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
 94 
 95         // 存储这页对应的maxtime
 96 
 97         weakSelf.maxtime = responseObject[@"info"][@"maxtime"];
 98 
 99         // 字典数组 -> 模型数组
100 
101         NSArray<XMGTopic *> *moreTopics = [XXTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];
102 
103         [weakSelf.topics addObjectsFromArray:moreTopics];
104 
105         // 插入新增的数据至数据库
106 
107         [[SQLiteManager shareSQLiteManager] saveTopics:moreTopics];
108 
109         // 刷新表格
110 
111         [weakSelf.tableView reloadData];
112 
113         
114 
115         // 让[刷新控件]结束刷新
116 
117         [weakSelf.tableView.mj_footer endRefreshing];
118 
119     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
120 
121         // 让[刷新控件]结束刷新
122 
123         [weakSelf.tableView.mj_footer endRefreshing];
124 
125  }];
126 
127 }
128 
129 #pragma mark - Table view data source
130 
131 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
132 
133     return self.topics.count;
134 
135 }
136 
137  
138 
139 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
140 
141     XXTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:XMGTopicCellId];
142 
143     cell.topic = self.topics[indexPath.row];
144 
145      return cell;
146 
147 }
148 
149 #pragma mark - 代理方法
150 
151 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
152 
153 {
154 
155     return self.topics[indexPath.row].cellHeight;
156 
157 }

 

这是面向模型的开发模式,模型类相关属性如下(局部):

 

@interface XXTopic : NSObject

/** 用户的名字 */

@property (nonatomic, copy) NSString *name;

/** 用户的头像 */

@property (nonatomic, copy) NSString *profile_image;

/** 帖子的文字内容 */

@property (nonatomic, copy) NSString *text;

/***** 额外增加的属性 - 方便开发 *****/

/** cell的高度 */

@property (nonatomic, assign) CGFloat cellHeight;

/** 中间内容的frame */

@property (nonatomic, assign) CGRect contentF;

@end

 

其实如果单纯的实现存储的功能,简单粗暴的方法就是直接将模型、或者原始的字典或数组整体进行存储。但是这样的话就不方便相关的统计、搜索,后期需要扩展这些功能的话笨重而不灵活。

所以这里就将模型属性一一对应进行sql的数据存储。设计一个工具类SQLiteManager,核心代码如下(本次旨在实现功能,代码还可以优化重构,或采用运行时机制减少侵入性):

#import "SQLiteManager.h"

#import <sqlite3.h>

#import "XXTopic.h"

 @interface SQLiteManager ()

{    sqlite3 *db;}

@end

 @implementation SQLiteManager

 + (instancetype)shareSQLiteManager {

    static SQLiteManager *instance = nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        instance = [[SQLiteManager alloc] init];

    });

    return  instance;

}

 

- (instancetype)init

{

    if (self = [super init]) {

        NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

        NSString *filePath = [path stringByAppendingPathComponent:@"demo.sqlite"];

        NSLog(@"%@", filePath);

        

        if (sqlite3_open([filePath UTF8String], &db) == SQLITE_OK)

        {    NSLog(@"打开成功");

            [self createTable];

        }

    }

    return  self;

}

 

- (BOOL)createTable {

    NSString *sql = @"CREATE TABLE IF NOT EXISTS t_topic(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, profile_image TEXT, text TEXT, cellHeight FLOAT);";

    return [self execSQL:sql];

}

 

- (BOOL)dropTable {

    NSString *sql  = @"drop table if exists t_stu;";

    return [self execSQL:sql];

}

  

- (BOOL)execSQL:(NSString *)sql {

    return  sqlite3_exec(db, [sql UTF8String], NULL, NULL, NULL) == SQLITE_OK;

}

 

- (void)saveTopics:(NSArray *)topics {    

    for(XXTopic* topic in topics) {

        NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_topic(name, profile_image, text, cellHeight) VALUES('%@', '%@', '%@', %f);", topic.name, topic.profile_image, topic.text, topic.cellHeight];

         sqlite3_stmt *stmt = nil;

        if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil) != SQLITE_OK)    return;

        if (sqlite3_step(stmt) == SQLITE_DONE){            

            NSLog(@"插入一条记录成功!");

       }

           sqlite3_finalize(stmt);

 }

}

 

  // 返回模型数组

- (NSArray *)getTopics  {

      NSString *sql = @"select * from t_topic;";

     // 准备语句

    sqlite3_stmt *stmt = nil;

    if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil) != SQLITE_OK)    {       

        NSLog(@"准备语句创建失败!");

        return nil;

    }    

    NSMutableArray *arrM = [NSMutableArray array];

    while (sqlite3_step(stmt) == SQLITE_ROW) {

        int count = sqlite3_column_count(stmt);

   id value;

   for (int i = 0; i < count; ++i) {        

        const char *cName = sqlite3_column_name(stmt, i);

        NSString *columnNameStr = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];

        NSLog(@"ddddddd%@",columnNameStr);

        int type = sqlite3_column_type(stmt, i);

        switch (type) {

            case SQLITE_INTEGER:

            {   int value = sqlite3_column_int(stmt, i);

            }

                break;

             case SQLITE_FLOAT:

            {   double value = sqlite3_column_double(stmt, i);

           }

                break;

            case SQLITE3_TEXT:

            {

                const char *textValue = sqlite3_column_text(stmt, i);

                value = [NSString stringWithCString:textValue encoding:NSUTF8StringEncoding];

            }

                break;

            case SQLITE_NULL:

                break;

              default:

                break;

        }

        XXTopic *topicItem = [[XXTopic alloc]init];

        if ([columnNameStr isEqualToString: @"name"]) {

            topicItem.name = (NSString *)value;

        }

        if ([columnNameStr isEqualToString: @"profile_image"]) {

            topicItem.profile_image = (NSString *)value;

        }

        if ([columnNameStr isEqualToString: @"text"]) {

            topicItem.text = (NSString *)value;

         }

        if ([columnNameStr isEqualToString: @"cellHeight"]) {

         topicItem.cellHeight = [value  doubleValue];

          }

        [arrM addObject:topicItem];

     }

    }

return  [arrM copy];

}

@end

 

 现在就可以实现存储的这两个方法了。

- (NSArray *)getFromeSql  {

if (topics != nil) return ;

return [[SQLiteManager shareSQLiteManager] getTopics];

}

- (void)saveToSql:(NSArray *)topics {

        [[SQLiteManager shareSQLiteManager] dropTable];

       //删除表后需要重新创建表单

        [[SQLiteManager shareSQLiteManager] createTable] ;

        [[SQLiteManager shareSQLiteManager] saveTopics:weakSelf.topics];

}

 

posted on 2017-01-08 21:55  imsz5460  阅读(418)  评论(0编辑  收藏  举报