zoukankan      html  css  js  c++  java
  • 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];
    
    }
    
  • 相关阅读:
    Atitit 数据库view视图使用推荐规范与最佳实践与方法
    Atitit mybatis快速开发 的sql api接口
    一个数据包经过路由器和交换机各会发生什么变化
    c preprocessor
    A database of opensource HTTP proxies written in python.
    google chrome os下载
    一道笔试题多字串查找
    一个老题:将正整数n分为若干num个不同的正整数之和
    web dev framework
    memory leakage
  • 原文地址:https://www.cnblogs.com/imsz/p/6262857.html
Copyright © 2011-2022 走看看