runtime 任意类型 model 数据库方便存储

//这里边直接上代码 之后我在慢慢地讲解  之后我的QQ:378254160 我有DEMO 方便你们的使用联系我备注 runtime+数据库+任意model类型  当然有时候也是有局限的

//DataBasehandle.m

#import "DataBasehandle.h"

#import <objc/runtime.h>

#import <sqlite3.h>

//http://www.sjsjw.com/kf_mobile/article/4_17043_17742.asp

@interface DataBasehandle ()

@property (nonatomic, copy)NSString *tabelename;

@property (nonatomic, copy)NSDictionary *dic;

@end

@implementation DataBasehandle

/* 获取对象的所有属性和属性内容 */

//这个方法 返回的是一个大字典 里边放着所有不为空 的属性和他们的值 属性是Key 值 是 Value

- (NSDictionary *)getAllPropertiesAndVaulesModel:(id)model

{

    NSMutableDictionary *props = [NSMutableDictionary dictionary];

    unsigned int outCount, i;

    objc_property_t *properties = class_copyPropertyList([model class], &outCount);

    for (i = 0; i<outCount; i++)

    {

        objc_property_t property = properties[i];

        const char* char_f =property_getName(property);

        NSString *propertyName = [NSString stringWithUTF8String:char_f];

        id propertyValue = [model valueForKey:(NSString *)propertyName];

        

        /*下面注释的这条语句是这个函数 最开始的语句 目的获取值不为空的属性 我修改之后就可以获取model全部属

         性当然model 属性必须全部写成NSString 类型的 不管实际的类型是什么!*/

         //if (propertyValue) [props setObject:propertyValue forKey:propertyName];

        

        //这样做 就可以获取model对象的全部属性!!!! 下面的代码是我修改的!

        if (propertyValue == nil) {

            [props setObject:@"nil" forKey:propertyName];

        }else

        {

          [props setObject:propertyValue forKey:propertyName];

        }

    }

    free(properties);

    return props;

}

static DataBasehandle * shareDataManager = nil;

//这样可以防止多进程的访问 GCD 写法!!!!!

+(DataBasehandle *) sharedDataBasehandle

{

    static dispatch_once_t once;

    dispatch_once(&once

                  , ^{

                       if (shareDataManager == nil)

                      {

                          shareDataManager = [ [ DataBasehandle alloc ] init ];

                      }

                      

                  });

    return shareDataManager;

}

static sqlite3 *db = nil;

-(void)openDB

{

    //已经打开完了之后 就不需要再打开了

    if(db != nil)return;

    //获取沙河路径 拼接一个文件的名字

    NSString *document = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

    NSString *path = [document stringByAppendingPathComponent:[NSString stringWithFormat:@"/%@",@"adress.sqlite"]];

    NSLog(@"%@",path);

    //打开数据库 初始化数据库了 已经 静态区不为nil了

    int result = sqlite3_open(path.UTF8String, &db);

    if(result == SQLITE_OK)

    {

        //打开成功!

        NSLog(@"打开成功");

    }else

    {

        NSLog(@"打开失败");

    }

}

-(void)closeDB

{

    int result = sqlite3_close(db);

    if (result == SQLITE_OK) {

        NSLog(@"关闭成功");

    }else

    {

        NSLog(@"关闭失败");

    }

}

//根据表的名字 和 运行时的model对象 创建一个表!

-(void)createtableName:(NSString *)name Model:(id)model

{

    //根据模型拼接字符串

    NSDictionary *dicstart = [self getAllPropertiesAndVaulesModel:model];

    //这里 必须这样操作一下 变成不可变的字典要不然 遍历的时候 表属性的顺序对应不上!

    NSDictionary *dic = [NSDictionary dictionaryWithDictionary:dicstart];

    self.dic = dic;

    self.tabelename = name;

    NSMutableString *str = [NSMutableString string];

    

    for (id obj in [dic allKeys] )

    {

      [str appendFormat:@" %@ text,",obj];

    }

    NSString *str1 = [str substringToIndex:str.length-1];

    //创建表的SQL语句 CREATE TABLE + 表名(字段 类型 ,...,字段 类型)

    NSString *createStr = [NSString stringWithFormat:@"CREATE TABLE  IF NOT EXISTS  %@(%@)",name,str1];

    NSLog(@"createStr === %@",createStr );

    //第一个参数:在那个数据库里边操作

    //第二个参数:代表要去执行那一条SQL 语句

    //第三个参数:代表那个回调函数

    //第四个参数:回调的一些参数

    //第五个参数:错误信息

    char *error = NULL;

    int result = sqlite3_exec(db, createStr.UTF8String, NULL, NULL, &error);

     printf("massage = %s",error);

    if(result == SQLITE_OK)

    {

        NSLog(@"创表成功");

    }else

    {

        NSLog(@"创表失败");

    }

}

//根据当前创建的表的名字 和运行时model对象 把model对象写入数据库

-(void)insertIntoTableModel:(id)model

{

    NSDictionary *dicstart = [self getAllPropertiesAndVaulesModel:model];

    NSDictionary *dic = [NSDictionary dictionaryWithDictionary:dicstart];

    NSString *name = self.tabelename;

    NSMutableString *strkey = [NSMutableString string];

    NSMutableString *strvalue = [NSMutableString string];

    for (id obj in [dic allKeys] )

    {

        [strkey appendFormat:@" %@,",obj];

        [strvalue appendFormat:@" '%@',",[dic objectForKey:obj]];

    }

    NSString *strkey1 = [strkey substringToIndex:strkey.length-1];

    NSString *strvalue1 = [strvalue substringToIndex:strvalue.length-1];

    

    //插入语句

    NSString *insertStr = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES(%@)",name,strkey1,strvalue1];

    //第一个参数:在那个数据库里边操作

    //第二个参数:代表要去执行那一条SQL 语句

    //第三个参数:代表那个回调函数

    //第四个参数:回调的一些参数

    //第五个参数:错误信息

    NSLog(@"%@",strkey1);

    char *message = NULL;

    int result = sqlite3_exec(db, insertStr.UTF8String, NULL, NULL, &message);

    printf("massage ======= %s",message);

    if(result == SQLITE_OK)

    {

        NSLog(@"插入成功");

    }else

    {

        NSLog(@"插入失败");

    }

}

//根据当前的表的名字和运行时model对象  删除 model对象所对应的 一条数据!

-(void)deleteFromTableModel:(id )model

{

    NSDictionary *dicstart = [self getAllPropertiesAndVaulesModel:model];

    NSDictionary *dic = [NSDictionary dictionaryWithDictionary:dicstart];

    NSString *name = self.tabelename;

    NSMutableString *strkey = [NSMutableString string];

    for (id obj in [dic allKeys] )

    {

        if ([ [dic objectForKey:obj ] isKindOfClass:[NSNumber class]] || [ [dic objectForKey:obj ] isKindOfClass:[NSString class]]   )

        {

            [strkey appendFormat:@" %@ = '%@' and",obj,[dic objectForKey:obj]];

        }

    }

        //去掉最后 四个字符

    NSString *strNew = [strkey substringToIndex:strkey.length-4];

    NSString *deleteStr = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@",name,strNew];

    char *message = NULL;

    int result = sqlite3_exec(db, deleteStr.UTF8String, NULL, NULL, &message);

    printf("%s",message);

    if(result == SQLITE_OK)

    {

        NSLog(@"删除成功");

    }else

    {

        NSLog(@"删除失败");

    }

}

//根据 运行时的model对象 和表的名字 获取所有的 model对象数组

-(NSArray *)selectAllTableModel:(id)model

{

     NSMutableArray  *dataArray = nil;

    //根据模型拼接字符串

    NSDictionary *adddic = self.dic;

    NSString *name = self.tabelename;

    // 查询SQL 语句

    // SELECT * FROM + 表名

    NSString *selectStr = [NSString stringWithFormat:@"SELECT * FROM %@",name];

    sqlite3_stmt *stmt = nil;

    int result = sqlite3_prepare(db, selectStr.UTF8String, -1, &stmt, NULL);

    if (result == SQLITE_OK)

    {

        //定义一个泛型 的指针 在下面会初始化这个泛型具体是什么类型的!不用担心!!!!

        id Model;

        //初始化数组

        dataArray = [NSMutableArray array ];

        

        while (sqlite3_step(stmt) == SQLITE_ROW)

        {

            //这里做的 工作最重要 要不然保存的模型 你是没办法拿到值的!!!!

            //这里根据runtime运行时 创建运行时的对象 这样就可以面对所有的对象!!!! 哈哈哈!

            model = [[ [model class] alloc] init];

            Model = model;

            //记录 表一条数据的位置每次循环的时候 增加!!!!!

            int i = 0;

            for (NSString *key in [adddic allKeys])

            {

                if ([[adddic objectForKey:key] isKindOfClass:[NSString class]] || [[adddic objectForKey:key] isKindOfClass:[NSNumber class]]) {

                  

                    if ([[adddic objectForKey:key] isKindOfClass:[NSString class]])

                    {

                        //这里注意一下可能是 NULL 空的 之后处理一下 避免KVC crash

                        if ((const char *)sqlite3_column_text(stmt, i) == NULL) {

                            [Model setValue:@" " forKey:key];

                        }else

                        {

                            NSString *string = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, i)];

                            [Model setValue:string forKey:key];

                        }

                    } else

                    {

                        NSInteger index = sqlite3_column_int(stmt, i);

                        //这里不用担心 一定能

                        NSString *string = [NSString stringWithFormat:@"%ld", (long)index];

                        [Model setValue:string forKey:key];

                    }

                    i++;

                }

            }

            [dataArray addObject:Model];

          }

    }

    //释放伴随指针

    sqlite3_finalize(stmt);

    return dataArray;

}

 @end

//DataBasehandle.h 中的文件 

#import <Foundation/Foundation.h>

@interface DataBasehandle : NSObject

+(DataBasehandle *) sharedDataBasehandle;

-(void)openDB;

-(void)closeDB;

/* 获取对象的所有属性和属性内容 */

- (NSDictionary *)getAllPropertiesAndVaulesModel:(id)model;

-(void)createtableName:(NSString *)name Model:(id)model;

-(void)insertIntoTableModel:(id )model;

-(void)deleteFromTableModel:(id )model;

-(NSArray *)selectAllTableModel:(id)model;

 @end

//根据我写的这个 工具类 你可以很方便的存储 根据任意类型的 model 存储到数据库里边 而不用担心数据库不用写 当然前提是你非常明白runtime 运行机制才行  你可以直接存储任意类型的model 当然前提model 里边的所有属性都是 NSString *obj; 类型的这是网络下载下来的数据,当然网络下载的数据可能是 NSNumber类型的 但是不用担心 用NSString接收 之后 显示的时候用 格式化字符串 皆可以解决这个问题!

下面说一下 这个工具类的用法! 很简单!如果现在没有 我就以后会加上的 当然也可以联系我!

//首先创建 一个工具类

    db = [[DataBasehandle alloc] init];

    [db openDB];

    //为了获取这个类 运行时的属性与对象值 创建model而已

    model = [[ActivityModelevent1 alloc] init];

    //创建 固定格式的表 这里注意一个类型的model 对应一个表格的名字

    [db createtableName:@"activitymodel" Model:model];

   //读取数据库里边的所有 model 组成的数组 变成可变数组 付给self.dataArray

    self.dataArray =  [NSMutableArray arrayWithArray:[db selectAllTableModel:model]];

 当然你也可以 增加和修改 如下

      //创建一个新的model1 任意类型都可以 你的model 但是必须和你的 表名 是一直的 这也就是 runtime 的优点 也是缺点

      model1 = [[ActivityModelevent1 alloc] init];

//增加 一个model1 在当前表名

      [db insertIntoTableModel:model1];

//删除 一个model1 在当前表名

      [db deleteFromTableModel:model1];

通过上面的简单的操作 根据runtime 运行时的好处 大家就可以 通过我的工具类 不必操作 数据库 就能把 model 数据存储和 删除了 非常简单! 当然这样虽然方便 也有他的弊端 大家了解了我的 思想就会 非常明白这个 弊端了 ,劝大家还是好好学习一下Sqlite 这才是王道,我现在懒得学习哈哈,就这样,大家下次见。

原文地址:https://www.cnblogs.com/yuzhouwjl/p/4868499.html