iOS开发之数据存取(一)——SQLite

2023-11-18

概览

在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储到数据库。例如前面IOS开发系列—Objective-C之Foundation框架的文章中提到归档、plist文件存储,包括偏好设置其本质都是存储为文件,只是说归档或者plist文件存储可以选择保存到沙盒中,而偏好设置系统已经规定只能保存到沙盒的Library/Preferences目录。当然,文件存储并不作为本文的重点内容。本文重点还是说数据库存储,做过数据库开发的朋友应该知道,可以通过SQL直接访问数据库,也可以通过ORM进行对象关系映射访问数据库。这两种方式恰恰对应iOS中SQLite和Core Data的内容,在此将重点进行分析:

1.SQLite

2.Core Data

3.FMDB

SQLite

SQLite是目前主流的嵌入式关系型数据库,其最主要的特点就是轻量级、跨平台,当前很多嵌入式操作系统都将其作为数据库首选。虽然SQLite是一款轻型数据库,但是其功能也绝不亚于很多大型关系数据库。学习数据库就要学习其相关的定义、操作、查询语言,也就是大家日常说得SQL语句。和其他数据库相比,SQLite中的SQL语法并没有太大的差别,因此这里对于SQL语句的内容不会过多赘述,大家可以参考SQLite中其他SQL相关的内容,这里还是重点讲解iOS中如何使用SQLite构建应用程序。先看一下SQLite数据库的几个特点:

1.基于C语言开发的轻型数据库

2.在iOS中需要使用C语言语法进行数据库操作、访问(无法使用ObjC直接访问,因为libqlite3框架基于C语言编写)

3.SQLite中采用的是动态数据类型,即使创建时定义了一种类型,在实际操作时也可以存储其他类型,但是推荐建库时使用合适的类型(特别是应用需要考虑跨平台的情况时)

4.建立连接后通常不需要关闭连接(尽管可以手动关闭)

要使用SQLite很简单,如果在Mac OSX上使用可以考虑到SQLite官方网站下载命令行工具,也可以使用类似于SQLiteManager、Navicat for SQLite等工具。为了方便大家开发调试,建议在开发环境中安装上述工具。

在iOS中操作SQLite数据库可以分为以下几步(注意先在项目中导入libsqlite3框架):

1.打开数据库,利用sqlite3_open()打开数据库会指定一个数据库文件保存路径,如果文件存在则直接打开,否则创建并打开。打开数据库会得到一个sqlite3类型的对象,后面需要借助这个对象进行其他操作。

2.执行SQL语句,执行SQL语句又包括有返回值的语句和无返回值语句。

3.对于无返回值的语句(如增加、删除、修改等)直接通过sqlite3_exec()函数执行;

4.对于有返回值的语句则首先通过sqlite3_prepare_v2()进行sql语句评估(语法检测),然后通过sqlite3_step()依次取出查询结果的每一行数据,对于每行数据都可以通过对应的sqlite3_column_类型()方法获得对应列的数据,如此反复循环直到遍历完成。当然,最后需要释放句柄。

在整个操作过程中无需管理数据库连接,对于嵌入式SQLite操作是持久连接(尽管可以通过sqlite3_close()关闭),不需要开发人员自己释放连接。纵观整个操作过程,其实与其他平台的开发没有明显的区别,较为麻烦的就是数据读取,在iOS平台中使用C进行数据读取采用了游标的形式,每次只能读取一行数据,较为麻烦。因此实际开发中不妨对这些操作进行封装:

KCDbManager.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//
//  DbManager.h
//  DataAccess
//
//  Created by Kenshin Cui on 14-3-29.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#import "KCSingleton.h"
@interface KCDbManager : NSObject
singleton_interface(KCDbManager);
#pragma mark - 属性
#pragma mark 数据库引用,使用它进行数据库操作
@property (nonatomic) sqlite3 *database;
#pragma mark - 共有方法
/**
  *  打开数据库
  *
  *  @param dbname 数据库名称
  */
-(void)openDb:(NSString *)dbname;
/**
  *  执行无返回值的sql
  *
  *  @param sql sql语句
  */
-(void)executeNonQuery:(NSString *)sql;
/**
  *  执行有返回值的sql
  *
  *  @param sql sql语句
  *
  *  @return 查询结果
  */
-(NSArray *)executeQuery:(NSString *)sql;
@end

KCDbManager.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//
//  DbManager.m
//  DataAccess
//
//  Created by Kenshin Cui on 14-3-29.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCDbManager.h"
#import <sqlite3.h>
#import "KCSingleton.h"
#import "KCAppConfig.h"
#ifndef kDatabaseName
#define kDatabaseName @"myDatabase.db"
#endif
@interface KCDbManager()
@end
@implementation KCDbManager
singleton_implementation(KCDbManager)
#pragma mark 重写初始化方法
-(instancetype)init{
     KCDbManager *manager;
     if ((manager=[ super  init]))
     {
         [manager openDb:kDatabaseName];
     }
     return  manager;
}
-(void)openDb:(NSString *)dbname{
     //取得数据库保存路径,通常保存沙盒Documents目录
     NSString *directory=[NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) firstObject];
     NSLog(@ "%@" ,directory);
     NSString *filePath=[directory stringByAppendingPathComponent:dbname];
     //如果有数据库则直接打开,否则创建并打开(注意filePath是ObjC中的字符串,需要转化为C语言字符串类型)
     if  (SQLITE_OK ==sqlite3_open(filePath.UTF8String, &_database)) {
         NSLog(@ "数据库打开成功!" );
     } else {
         NSLog(@ "数据库打开失败!" );
     }
}
-(void)executeNonQuery:(NSString *)sql{
     char *error;
     //单步执行sql语句,用于插入、修改、删除
     if  (SQLITE_OK!=sqlite3_exec(_database, sql.UTF8String, NULL, NULL,&error)) {
         NSLog(@ "执行SQL语句过程中发生错误!错误信息:%s" ,error);
     }
}
-(NSArray *)executeQuery:(NSString *)sql{
     NSMutableArray *rows=[NSMutableArray array]; //数据行
     
     //评估语法正确性
     sqlite3_stmt *stmt;
     //检查语法正确性
     if  (SQLITE_OK==sqlite3_prepare_v2(_database, sql.UTF8String, -1, &stmt, NULL)) {
         //单步执行sql语句
         while  (SQLITE_ROW==sqlite3_step(stmt)) {
             int columnCount= sqlite3_column_count(stmt);
             NSMutableDictionary *dic=[NSMutableDictionary dictionary];
             for  (int i=0; i<columnCount; i++) {
                 const char *name= sqlite3_column_name(stmt, i); //取得列名
                 const unsigned char *value= sqlite3_column_text(stmt, i); //取得某列的值
                 dic[[NSString stringWithUTF8String:name]]=[NSString stringWithUTF8String:(const char *)value];
             }
             [rows addObject:dic];
         }
     }
     
     //释放句柄
     sqlite3_finalize(stmt);
     
     return  rows;
}
@end

在上面的类中对于数据库操作进行了封装,封装之后数据操作更加方便,同时所有的语法都由C转换成了ObjC。

下面仍然以微博查看为例进行SQLite演示。当然实际开发中微博数据是从网络读取的,但是考虑到缓存问题,通常会选择将微博数据保存到本地,下面的Demo演示了将数据存放到本地数据库以及数据读取的过程。当然,实际开发中并不会在视图控制器中直接调用数据库操作方法,在这里通常会引入两个概念Model和Service。Model自不必多说,就是MVC中的模型。而Service指的是操作数据库的服务层,它封装了对于Model的基本操作方法,实现具体的业务逻辑。为了解耦,在控制器中是不会直接接触数据库的,控制器中只和模型(模型是领域的抽象)、服务对象有关系,借助服务层对模型进行各类操作,模型的操作反应到数据库中就是对表中数据的操作。具体关系如下:

404.jpg

要完成上述功能,首先定义一个应用程序全局对象进行数据库、表的创建。为了避免每次都创建数据库和表出错,这里利用了偏好设置进行保存当前创建状态(其实这也是数据存储的一部分),如果创建过了数据库则不再创建,否则创建数据库和表。

KCDatabaseCreator.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//
//  KCDatabaseCreator.m
//  DataAccess
//
//  Created by Kenshin Cui on 14-3-29.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCDatabaseCreator.h"
#import "KCDbManager.h"
@implementation KCDatabaseCreator
+(void)initDatabase{
     NSString *key=@ "IsCreatedDb" ;
     NSUserDefaults *defaults=[[NSUserDefaults alloc]init];
     if  ([[defaults valueForKey:key] intValue]!=1) {
         [self createUserTable];
         [self createStatusTable];
         [defaults setValue:@1 forKey:key];
     }
}
+(void)createUserTable{
     NSString *sql=@ "CREATE TABLE User (Id integer PRIMARY KEY AUTOINCREMENT,name text,screenName text, profileImageUrl text,mbtype text,city text)" ;
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
+(void)createStatusTable{
     NSString *sql=@ "CREATE TABLE Status (Id integer PRIMARY KEY AUTOINCREMENT,source text,createdAt date,\"text\" text,user integer REFERENCES User (Id))" ;
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
@end

其次,定义数据模型,这里定义用户User和微博Status两个数据模型类。注意模型应该尽量保持其单纯性,仅仅是简单的POCO,不要引入视图、控制器等相关内容。

KCUser.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//
//  KCUser.h
//  UrlConnection
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface KCUser : NSObject
#pragma mark 编号
@property (nonatomic,strong) NSNumber *Id;
#pragma mark 用户名
@property (nonatomic,copy) NSString *name;
#pragma mark 用户昵称
@property (nonatomic,copy) NSString *screenName;
#pragma mark 头像
@property (nonatomic,copy) NSString *profileImageUrl;
#pragma mark 会员类型
@property (nonatomic,copy) NSString *mbtype;
#pragma mark 城市
@property (nonatomic,copy) NSString *city;
#pragma mark - 动态方法
/**
  *  初始化用户
  *
  *  @param name 用户名
  *  @param city 所在城市
  *
  *  @return 用户对象
  */
-(KCUser *)initWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city;
/**
  *  使用字典初始化用户对象
  *
  *  @param dic 用户数据
  *
  *  @return 用户对象
  */
-(KCUser *)initWithDictionary:(NSDictionary *)dic;
#pragma mark - 静态方法
+(KCUser *)userWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city;
@end

KCUser.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//
//  KCUser.m
//  UrlConnection
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCUser.h"
@implementation KCUser
-(KCUser *)initWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{
     if  (self=[ super  init]) {
         self.name=name;
         self.screenName=screenName;
         self.profileImageUrl=profileImageUrl;
         self.mbtype=mbtype;
         self.city=city;
     }
     return  self;
}
-(KCUser *)initWithDictionary:(NSDictionary *)dic{
     if  (self=[ super  init]) {
         [self setValuesForKeysWithDictionary:dic];
     }
     return  self;
}
+(KCUser *)userWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{
     KCUser *user=[[KCUser alloc]initWithName:name screenName:screenName profileImageUrl:profileImageUrl mbtype:mbtype city:city];
     return  user;
}
@end

KCStatus.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//
//  KCStatus.h
//  UITableView
//
//  Created by Kenshin Cui on 14-3-1.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "KCUser.h"
@interface KCStatus : NSObject
#pragma mark - 属性
@property (nonatomic,strong) NSNumber *Id; //微博id
@property (nonatomic,strong) KCUser *user; //发送用户
@property (nonatomic,copy) NSString *createdAt; //创建时间
@property (nonatomic,copy) NSString *source; //设备来源
@property (nonatomic,copy) NSString *text; //微博内容
#pragma mark - 动态方法
/**
  *  初始化微博数据
  *
  *  @param createAt        创建日期
  *  @param source          来源
  *  @param text            微博内容
  *  @param user            发送用户
  *
  *  @return 微博对象
  */
-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user;
/**
  *  初始化微博数据
  *
  *  @param profileImageUrl 用户头像
  *  @param mbtype          会员类型
  *  @param createAt        创建日期
  *  @param source          来源
  *  @param text            微博内容
  *  @param userId          用户编号
  *
  *  @return 微博对象
  */
-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId;
/**
  *  使用字典初始化微博对象
  *
  *  @param dic 字典数据
  *
  *  @return 微博对象
  */
-(KCStatus *)initWithDictionary:(NSDictionary *)dic;
#pragma mark - 静态方法
/**
  *  初始化微博数据
  *
  *  @param createAt        创建日期
  *  @param source          来源
  *  @param text            微博内容
  *  @param user            发送用户
  *
  *  @return 微博对象
  */
+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user;
/**
  *  初始化微博数据
  *
  *  @param profileImageUrl 用户头像
  *  @param mbtype          会员类型
  *  @param createAt        创建日期
  *  @param source          来源
  *  @param text            微博内容
  *  @param userId          用户编号
  *
  *  @return 微博对象
  */
+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId;
@end

KCStatus.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//
//  KCStatus.m
//  UITableView
//
//  Created by Kenshin Cui on 14-3-1.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCStatus.h"
@implementation KCStatus
-(KCStatus *)initWithDictionary:(NSDictionary *)dic{
     if  (self=[ super  init]) {
         [self setValuesForKeysWithDictionary:dic];
         self.user=[[KCUser alloc]init];
         self.user.Id=dic[@ "user" ];
     }
     return  self;
}
-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user{
     if  (self=[ super  init]) {
         self.createdAt=createAt;
         self.source=source;
         self.text=text;
         self.user=user;
     }
     return  self;
}
-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId{
     if  (self=[ super  init]) {
         self.createdAt=createAt;
         self.source=source;
         self.text=text;
         KCUser *user=[[KCUser alloc]init];
         user.Id=[NSNumber numberWithInt:userId];
         self.user=user;
     }
     return  self;
}
-(NSString *)source{
     return  [NSString stringWithFormat:@ "来自 %@" ,_source];
}
+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user{
     KCStatus *status=[[KCStatus alloc]initWithCreateAt:createAt source:source text:text user:user];
     return  status;
}
+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId{
     KCStatus *status=[[KCStatus alloc]initWithCreateAt:createAt source:source text:text userId:userId];
     return  status;
}
@end

然后,编写服务类,进行数据的增、删、改、查操作,由于服务类方法同样不需要过多的配置,因此定义为单例,保证程序中只有一个实例即可。服务类中调用前面封装的数据库方法将对数据库的操作转换为对模型的操作。

KCUserService.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//
//  KCUserService.h
//  DataAccess
//
//  Created by Kenshin Cui on 14-3-29.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "KCUser.h"
#import "KCSingleton.h"
@interface KCUserService : NSObject
singleton_interface(KCUserService)
/**
  *  添加用户信息
  *
  *  @param user 用户对象
  */
-(void)addUser:(KCUser *)user;
/**
  *  删除用户
  *
  *  @param user 用户对象
  */
-(void)removeUser:(KCUser *)user;
/**
  *  根据用户名删除用户
  *
  *  @param name 用户名
  */
-(void)removeUserByName:(NSString *)name;
/**
  *  修改用户内容
  *
  *  @param user 用户对象
  */
-(void)modifyUser:(KCUser *)user;
/**
  *  根据用户编号取得用户
  *
  *  @param Id 用户编号
  *
  *  @return 用户对象
  */
-(KCUser *)getUserById:(int)Id;
/**
  *  根据用户名取得用户
  *
  *  @param name 用户名
  *
  *  @return 用户对象
  */
-(KCUser *)getUserByName:(NSString *)name;
@end

KCUserService.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//
//  KCUserService.m
//  DataAccess
//
//  Created by Kenshin Cui on 14-3-29.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCUserService.h"
#import "KCUser.h"
#import "KCDbManager.h"
@implementation KCUserService
singleton_implementation(KCUserService)
-(void)addUser:(KCUser *)user{
     NSString *sql=[NSString stringWithFormat:@ "INSERT INTO User (name,screenName, profileImageUrl,mbtype,city) VALUES('%@','%@','%@','%@','%@')" ,user.name,user.screenName, user.profileImageUrl,user.mbtype,user.city];
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
-(void)removeUser:(KCUser *)user{
     NSString *sql=[NSString stringWithFormat:@ "DELETE FROM User WHERE Id='%@'" ,user.Id];
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
-(void)removeUserByName:(NSString *)name{
     NSString *sql=[NSString stringWithFormat:@ "DELETE FROM User WHERE name='%@'" ,name];
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
-(void)modifyUser:(KCUser *)user{
     NSString *sql=[NSString stringWithFormat:@ "UPDATE User SET name='%@',screenName='%@',profileImageUrl='%@',mbtype='%@',city='%@' WHERE Id='%@'" ,user.name,user.screenName,user.profileImageUrl,user.mbtype,user.city,user.Id];
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
-(KCUser *)getUserById:(int)Id{
     KCUser *user=[[KCUser alloc]init];
     NSString *sql=[NSString stringWithFormat:@ "SELECT name,screenName,profileImageUrl,mbtype,city FROM User WHERE Id='%i'" , Id];
     NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql];
     if  (rows&&rows.count>0) {
         [user setValuesForKeysWithDictionary:rows[0]];
     }
     return  user;
}
-(KCUser *)getUserByName:(NSString *)name{
     KCUser *user=[[KCUser alloc]init];
     NSString *sql=[NSString stringWithFormat:@ "SELECT Id, name,screenName,profileImageUrl,mbtype,city FROM User WHERE name='%@'" , name];
     NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql];
     if  (rows&&rows.count>0) {
         [user setValuesForKeysWithDictionary:rows[0]];
     }
     return  user;
}
@end

KCStatusService.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//
//  KCStatusService.h
//  DataAccess
//
//  Created by Kenshin Cui on 14-3-29.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "KCSingleton.h"
@class KCStatus;
@interface KCStatusService : NSObject
singleton_interface(KCStatusService)
/**
  *  添加微博信息
  *
  *  @param status 微博对象
  */
-(void)addStatus:(KCStatus *)status;
/**
  *  删除微博
  *
  *  @param status 微博对象
  */
-(void)removeStatus:(KCStatus *)status;
/**
  *  修改微博内容
  *
  *  @param status 微博对象
  */
-(void)modifyStatus:(KCStatus *)status;
/**
  *  根据编号取得微博
  *
  *  @param Id 微博编号
  *
  *  @return 微博对象
  */
-(KCStatus *)getStatusById:(int)Id;
/**
  *  取得所有微博对象
  *
  *  @return 所有微博对象
  */
-(NSArray *)getAllStatus;
@end

KCStatusService.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//
//  KCStatusService.m
//  DataAccess
//
//  Created by Kenshin Cui on 14-3-29.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCStatusService.h"
#import "KCDbManager.h"
#import "KCStatus.h"
#import "KCUserService.h"
#import "KCSingleton.h"
@interface KCStatusService(){
     
}
@end
@implementation KCStatusService
singleton_implementation(KCStatusService)
-(void)addStatus:(KCStatus *)status{
     NSString *sql=[NSString stringWithFormat:@ "INSERT INTO Status (source,createdAt,\"text\" ,user) VALUES('%@','%@','%@','%@')" ,status.source,status.createdAt,status.text,status.user.Id];
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
-(void)removeStatus:(KCStatus *)status{
     NSString *sql=[NSString stringWithFormat:@ "DELETE FROM Status WHERE Id='%@'" ,status.Id];
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
-(void)modifyStatus:(KCStatus *)status{
     NSString *sql=[NSString stringWithFormat:@ "UPDATE Status SET source='%@',createdAt='%@',\"text\"='%@' ,user='%@' WHERE Id='%@'" ,status.source,status.createdAt,status.text,status.user, status.Id];
     [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
-(KCStatus *)getStatusById:(int)Id{
     KCStatus *status=[[KCStatus alloc]init];
     NSString *sql=[NSString stringWithFormat:@ "SELECT Id, source,createdAt,\"text\" ,user FROM Status WHERE Id='%i'" , Id];
     NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql];
     if  (rows&&rows.count>0) {
         [status setValuesForKeysWithDictionary:rows[0]];
         status.user=[[KCUserService sharedKCUserService] getUserById:[(NSNumber *)rows[0][@ "user" ] intValue]] ;
     }
     return  status;
}
-(NSArray *)getAllStatus{
     NSMutableArray *array=[NSMutableArray array];
     NSString *sql=@ "SELECT Id, source,createdAt,\"text\" ,user FROM Status ORDER BY Id" ;
     NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql];
     for  (NSDictionary *dic  in  rows) {
         KCStatus *status=[self getStatusById:[(NSNumber *)dic[@ "Id" ] intValue]];
         [array addObject:status];
     }
     return  array;
}
@end

最后,在视图控制器中调用相应的服务层进行各类数据操作,在下面的代码中分别演示了增、删、改、查四类操作。

KCMainViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//
//  KCMainTableViewController.m
//  DataAccess
//
//  Created by Kenshin Cui on 14-3-29.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCMainTableViewController.h"
#import "KCDbManager.h"
#import "KCDatabaseCreator.h"
#import "KCUser.h"
#import "KCStatus.h"
#import "KCUserService.h"
#import "KCStatusService.h"
#import "KCStatusTableViewCell.h"
@interface KCMainTableViewController (){
     NSArray *_status;
     NSMutableArray *_statusCells;
}
@end
@implementation KCMainTableViewController
- (void)viewDidLoad {
     [ super  viewDidLoad];
     [KCDatabaseCreator initDatabase];
     
//    [self addUsers];
//    [self removeUser];
//    [self modifyUserInfo];
     
//    [self addStatus];
     
     [self loadStatusData];
     
}
-(void)addUsers{
     KCUser *user1=[KCUser userWithName:@ "Binger"  screenName:@ "冰儿"  profileImageUrl:@ "binger.jpg"  mbtype:@ "mbtype.png"  city:@ "北京" ];
     [[KCUserService sharedKCUserService] addUser:user1];
     KCUser *user2=[KCUser userWithName:@ "Xiaona"  screenName:@ "小娜"  profileImageUrl:@ "xiaona.jpg"  mbtype:@ "mbtype.png"  city:@ "北京" ];
     [[KCUserService sharedKCUserService] addUser:user2];
     KCUser *user3=[KCUser userWithName:@ "Lily"  screenName:@ "丽丽"  profileImageUrl:@ "lily.jpg"  mbtype:@ "mbtype.png"  city:@ "北京" ];
     [[KCUserService sharedKCUserService] addUser:user3];
     KCUser *user4=[KCUser userWithName:@ "Qianmo"  screenName:@ "阡陌"  profileImageUrl:@ "qianmo.jpg"  mbtype:@ "mbtype.png"  city:@ "北京" ];
     [[KCUserService sharedKCUserService] addUser:user4];
     KCUser *user5=[KCUser userWithName:@ "Yanyue"  screenName:@ "炎月"  profileImageUrl:@ "yanyue.jpg"  mbtype:@ "mbtype.png"  city:@ "北京" ];
     [[KCUserService sharedKCUserService] addUser:user5];
}
-(void)addStatus{
     KCStatus *status1=[KCStatus statusWithCreateAt:@ "9:00"  source:@ "iPhone 6"  text:@ "一只雪猴在日本边泡温泉边玩iPhone的照片,获得了\"2014年野生动物摄影师\"大赛特等奖。一起来为猴子配个词"  userId:1];
     [[KCStatusService sharedKCStatusService] addStatus:status1];
     KCStatus *status2=[KCStatus statusWithCreateAt:@ "9:00"  source:@ "iPhone 6"  text:@ "一只雪猴在日本边泡温泉边玩iPhone的照片,获得了\"2014年野生动物摄影师\"大赛特等奖。一起来为猴子配个词"  userId:1];
     [[KCStatusService sharedKCStatusService] addStatus:status2];
     KCStatus *status3=[KCStatus statusWithCreateAt:@ "9:30"  source:@ "iPhone 6"  text:@ "【我们送iPhone6了 要求很简单】真心回馈粉丝,小编觉得现在最好的奖品就是iPhone6了。今起到12月31日,关注我们,转发微博,就有机会获iPhone6(奖品可能需要等待)!每月抽一台[鼓掌]。不费事,还是试试吧,万一中了呢"  userId:2];
     [[KCStatusService sharedKCStatusService] addStatus:status3];
     KCStatus *status4=[KCStatus statusWithCreateAt:@ "9:45"  source:@ "iPhone 6"  text:@ "重大新闻:蒂姆库克宣布出柜后,ISIS战士怒扔iPhone,沙特神职人员呼吁人们换回iPhone 4。[via Pan-Arabia Enquirer]"  userId:3];
     [[KCStatusService sharedKCStatusService] addStatus:status4];
     KCStatus *status5=[KCStatus statusWithCreateAt:@ "10:05"  source:@ "iPhone 6"  text:@ "小伙伴们,有谁知道怎么往Iphone4S里倒东西?倒入的东西又该在哪里找?用了Iphone这么长时间,还真的不知道怎么弄!有谁知道啊?谢谢!"  userId:4];
     [[KCStatusService sharedKCStatusService] addStatus:status5];
     KCStatus *status6=[KCStatus statusWithCreateAt:@ "10:07"  source:@ "iPhone 6"  text:@ "在音悦台iPhone客户端里发现一个悦单《Infinite 金明洙》,推荐给大家! "  userId:1];
     [[KCStatusService sharedKCStatusService] addStatus:status6];
     KCStatus *status7=[KCStatus statusWithCreateAt:@ "11:20"  source:@ "iPhone 6"  text:@ "如果sony吧mp3播放器产品发展下去,不贪图手头节目源的现实利益,就木有苹果的ipod,也就木有iphone。柯达类似的现实利益,不自我革命的案例也是一种巨头的宿命。"  userId:2];
     [[KCStatusService sharedKCStatusService] addStatus:status7];
     KCStatus *status8=[KCStatus statusWithCreateAt:@ "13:00"  source:@ "iPhone 6"  text:@ "【iPhone 7 Plus】新买的iPhone 7 Plus ,如何?够酷炫么?"  userId:2];
     [[KCStatusService sharedKCStatusService] addStatus:status8];
     KCStatus *status9=[KCStatus statusWithCreateAt:@ "13:24"  source:@ "iPhone 6"  text:@ "自拍神器#卡西欧TR500#,tr350S~价格美丽,行货,全国联保~iPhone6 iPhone6Plus卡西欧TR150 TR200 TR350 TR350S全面到货 招收各种代理![给力]微信:39017366"  userId:3];
     [[KCStatusService sharedKCStatusService] addStatus:status9];
     KCStatus *status10=[KCStatus statusWithCreateAt:@ "13:26"  source:@ "iPhone 6"  text:@ "猜到猴哥玩手机时所思所想者,再奖iPhone一部。(奖品由“2014年野生动物摄影师”评委会颁发)"  userId:3];
     [[KCStatusService sharedKCStatusService] addStatus:status10];
}
-(void)removeUser{
     //注意在SQLite中区分大小写
     [[KCUserService sharedKCUserService] removeUserByName:@ "Yanyue" ];
}
-(void)modifyUserInfo{
     KCUser *user1= [[KCUserService sharedKCUserService]getUserByName:@ "Xiaona" ];
     user1.city=@ "上海" ;
     [[KCUserService sharedKCUserService] modifyUser:user1];
     
     KCUser *user2= [[KCUserService sharedKCUserService]getUserByName:@ "Lily" ];
     user2.city=@ "深圳" ;
     [[KCUserService sharedKCUserService] modifyUser:user2];
}
#pragma mark 加载数据
-(void)loadStatusData{
     _statusCells=[[NSMutableArray alloc]init];
     _status=[[KCStatusService sharedKCStatusService]getAllStatus];
     [_status enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
         KCStatusTableViewCell *cell=[[KCStatusTableViewCell alloc]init];
         cell.status=(KCStatus *)obj;
         [_statusCells addObject:cell];
     }];
     NSLog(@ "%@" ,[_status lastObject]);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
     return  1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
     return  _status.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     static NSString *identtityKey=@ "myTableViewCellIdentityKey1" ;
     KCStatusTableViewCell *cell=[self.tableView dequeueReusableCellWithIdentifier:identtityKey];
     if (cell==nil){
         cell=[[KCStatusTableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identtityKey];
     }
     cell.status=_status[indexPath.row];
     return  cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
     return  ((KCStatusTableViewCell *)_statusCells[indexPath.row]).height;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
     return  20.0f;
}
@end

项目目录结构:

060906141277216.jpg

运行效果

060906162206843.jpg

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS开发之数据存取(一)——SQLite 的相关文章

  • 错误:更改核心数据模型后架构armv7的重复符号

    我有一个使用核心数据框架的应用程序 我工作得很好 我刚刚更改了数据模型 向一个实体添加一个属性 当我尝试构建它时 出现错误 duplicate symbol OBJC METACLASS AccountFolder in Users XXX
  • 关于窗口层次结构的警告

    我的调试器中出现这样的警告 这是什么意思 Warning Attempt to present
  • 检测 AvPlayer 何时切换比特率

    在我的应用程序中 我使用 AVPlayer 通过 HLS 协议读取一些流 m3u8 文件 我需要知道在流会话期间 客户端切换比特率多少次 我们假设客户端的带宽正在增加 因此客户端将切换到更高比特率的段 AVPlayer能检测到这个开关吗 T
  • 我怎样才能勾勒出文本字体?

    我想在边框 轮廓 中显示另一种颜色的文本 我正在尝试使用在 MapOverlayView 中显示文本 text drawAtPoint CGPointMake 0 30 withFont UIFont fontWithName Helvet
  • iphone:如何停止快门动画?

    我有两个问题 1 我想知道如何在相机加载时停止快门动画 我正在使用 UIImagePickerController 我已经参考了堆栈溢出的许多答案 但没有成功 2 我在相机中有一个自定义按钮 使用cameraOverlayView并想通过单
  • Google 地图 API -> OpenGLES 崩溃

    日志是从 Crashlytics 粘贴的 对于许多用户来说 崩溃经常发生 据我所知 它与设备 iOS 版本无关 我在我的代码中找不到任何错误 这似乎是纯粹的库问题 是 Google 地图 API 错误吗 我可以做些什么来修复它 或者我应该在
  • 避免 UIImage 的 imageNamed - 内存管理

    我正在经历这个链接 http akosma com 2009 01 28 10 iphone memory management tips 我遇到了一个点避免 UIImage 的 imageNamed 出于什么原因我们应该避免这种情况 它会
  • 如何反转 CGPath 的点顺序

    我想画一个圆圈 并用它打出字母 为此 我需要顺时针抚摸圆圈 逆时针抚摸字母 这一切都很好 但是当我使用 Core Text 获取字母路径时 我不知道如何从本质上反转该路径 不是镜像或旋转或任何东西 这很简单 我希望点笔画顺序是逆时针的 这实
  • Swift C 回调 - Swift 类指针的 takeUnretainedValue 或 takeRetainedValue

    我有一些UIView or UITableViewCell 里面我有 C 回调 例如 CCallback bridge self observer data gt Void in let mySelf Unmanaged
  • Swift 中的 UIAlert 自动消失?

    我有以下代码 Creates Alerts on screen for user func notifyUser title String message String gt Void let alert UIAlertController
  • IPV6 快速可达性

    我是 swift 和 xcode 的新手 并且我的应用程序因 IPV6 而被拒绝 性能 2 1 当我们执行以下操作时 您的应用程序会在运行 iOS 9 3 5 并连接到 IPv6 网络的 iPad 和 iPhone 上崩溃 具体来说 当我们
  • 在 iOS5 中使用 UISegmentedControl 切换 ViewController

    我正在尝试一些非常简单的事情 但不知何故我无法让它发挥作用 我尝试做的就是使用 UISegmentedControl 在 2 个视图控制器之间切换 就像您可以在 App Store 应用程序的 突出显示 选项卡中看到的那样 我正在使用 iO
  • 如何提高包含大量小图像的 UCollectionView 的性能?

    在我的 iOS 应用程序中我有UICollectionView显示大约 1200 个小 35x35 点 图像 图像存储在应用程序包中 我正确地重用了UICollectionViewCell但仍然存在性能问题 具体取决于我处理图像加载的方式
  • 奇怪的 UITableView 插入/删除行动画

    在带有动画 UITableView RowAnimationTop 的 UITableView 中插入 删除 UITableViewCell 时 我看到了奇怪的效果 当要插入的单元格比上面的单元格大得多时 就会发生动画故障 显示模拟器中的故
  • 获取对 iOS 应用程序中最顶层视图/窗口的引用

    我正在创建一个可重用的框架 用于在 iOS 应用程序中显示通知 我希望将通知视图添加到应用程序中其他所有内容的顶部 有点像 UIAlertView 当我初始化监听 NSNotification 事件并添加视图作为响应的管理器时 我需要获取对
  • React Native:不透明视图内的透明视图

    我想用不透明框架和透明中心显示相机的视图 就像图片中的一样 黑色部分是相机的视图 我正在寻找具有纯反应本机组件的解决方案 没有额外的库 例如https github com gilbox react native masked view h
  • 如何在 ios 7 上更改重新排序控制图像

    我正在寻找一种更改重新排序控件图像和大小的方法 我使用此代码来更改重新排序图像 void tableView UITableView tableView willDisplayCell UITableViewCell cell forRow
  • 迁移到 ARC 时如何解决“选择器'performSelector:withObject:afterDelay:'没有已知的实例方法”?

    ARC 迁移工具在开始迁移之前拒绝接受此代码 self delegate performSelector selector overlayDismissed withObject self afterDelay 0 委托被迫使用协议来实现此
  • -[EAGLContext renderbufferStorage:fromDrawable:] 第二次失败?

    我正在开发一个 iOS openGL ES 应用程序 我正在做通常的 EAGLView ES2Render 的事情 启动时 使用以下代码成功创建 frambuffer BOOL createFramebuffers EAGLContext
  • 用 UIView 像翻书一样翻页?

    我正在尝试在之间切换UIViews让它看起来就像你正在翻书的一页 The UIViewAnimationTransitionCurlUp如果我能让它向左或向右卷曲 那就非常接近了 这可能吗 我尝试过使用CATRansition但没有一种动画

随机推荐

  • 蓝桥杯 c/c++ 算法提高 最长滑雪道

    算法提高 最长滑雪道 资源限制 时间限制 1 0s 内存限制 256 0MB 问题描述 小袁非常喜欢滑雪 因为滑雪很刺激 为了获得速度 滑的区域必须向下倾斜 而且当你滑到坡底 你不得不再次走上坡或者等待升降机来载你 小袁想知道在某个区域中最
  • CSS(非)实用技巧——背景与边框

    参考 CSS揭秘 CSS Secrets 背景 CSS3 尽管 CSS3 这个名词非常流行 但它严格意义上并不是一个规范 因为在CSS2之后 CSS这门语言已经庞大到无法放进单个规范中了 CSS工作组将其分成不同的模块 其中在CSS2 1已
  • 图书馆数据库服务器设备性能分析,高校图书馆数据库利用率统计与绩效的浅析.doc...

    PAGE PAGE 1 高校图书馆数据库利用率统计与绩效的浅析 摘要 现代的社会是一个信息社会 社会经济发展步伐和高科技信息技术的发展已经超过了我们想象的能力范围 随着越来越多的高校图书馆的建立 为了更好的服务学生和学校教师的教学 科研工作
  • 线程的优先级

    package com kuang Demo05 测试线程的优先级 public class TestPriority public static void main String args System out println Threa
  • Eclipse 从Debug模式中退出

    如果您尚未处于Java透视图中 请在主菜单中选择Window gt Open Perspective gt Java或单击下面的 Click to Perform 链接 Eclipse的右侧有新手教学 简单来说就是菜单栏里按顺序点击 就能切
  • tensorflow-gpu版本详细安装教程(Win10,Python3.7.9,cuda11.2,cudnn8.1.0)

    目录 cuda安装 cudnn安装 python安装 tensorflow安装 cuda安装下载 首先要注意自己的显卡支持cuda的版本 可以这样自查 可以看到适合自己的cuda版本 这里我选择了cuda11 2版本 cuda安装包下载链接
  • 虚拟机与物理机的三种连接方式

    引言 vmware为我们提供了三种网络工作模式 它们分别是 Bridged 桥接模式 NAT 网络地址转换模式 Host Only 仅主机模式 我们在刚刚接触它们的时候 常常被搞的晕头转向 今天我就为大家介绍一下这三种连接方式 桥接模式 桥
  • web前台传递时间日期格式转换为java.util.date,initBinder或者@DateTimeFormat方式

    方法1 根据前台传递的日期格式 在后台创建一个参数绑定的方法 将对应的日期字符串 转换为 java util date 从界面传递参数到spring后台controller类中 日期格式默认只能传long类型的时间戳 如果想要传递格式化的日
  • 阿里云ECS部署Nginx配置域名访问

    目录 前言 环境 具体步骤 服务器 域名 SSL证书 Nginx配置 前言 记录下阿里云服务器建站的过程 回回建 回回忘 尴尬 环境 ECS Centos7 6 Nginx 具体步骤 服务器 首先 需要购买一台服务器 域名 需要购买一个域名
  • Python unicode equal comparison failed

    用python进行判断的时候 如果包含中文会报错 name 这里是中文 if i name print right 修改成 name 这里是中文 decode utf 8
  • 聊聊undefined 和 undeclared

    一 undefined 在Js中 有两个表示 空 的值undefined和null 其中比较有用的是 undefined undefined 是一个值为 undefined 的类型 JavaScript语言也定义了一个全局变量 它的值是 u
  • oracle存储过程----异常的写法介绍

    上一篇 oracle存储过程 case条件控制语句的用法 oracle存储过程 异常介绍 参考PL SQL 存储过程中的异常来自于程序本身 也有的来自开发人员自定义的数据 而所有的这些错误我们称之为异常 编译时的错误不能称为异常 esmp
  • kubeadm配置虚拟机k8s集群

    环境 centos7 vm pro windows terminal termius 虚拟机 硬件配置 2核2G 实验用 具体可根据电脑调整 配置3台 master01 node01 node02 通过克隆虚拟机直接复制 配置通一项以后建议
  • spark-submit 报错 Initial job has not accepted any resources

    spark submit 报这样的错误 WARN scheduler TaskSchedulerImpl Initial job has not accepted any resources check your cluster UI to
  • [pytest源码4]-pluggy之Plugin注册逻辑分析

    前言 本篇将详细对plugin的注册逻辑进行分析 个人拙见 有错请各位指出 如果的我的文章对您有帮助 不符动动您的金手指给个Star 予人玫瑰 手有余香 不胜感激 GitHub pluggy注册逻辑分析性 我们来详细分析一下plugin的注
  • 有道无术,术尚可求,有术无道,止于术

    有道无术 术尚可求 有术无道 止于术 老子 道德经 道和术在一直是一个引起人们广泛讨论的话题 古今中外许多人对此皆有见解 道和术放在道德和能力上来说 到底哪个更重要许多名人名言中都有阐述 1 因为道德是做人的根本 根本一坏 纵然使你有一些学
  • 一文精通常量池

    大家好 今天来和大家一起探索下Java的常量池 在阅读本篇之前 我为大家准备了一个测试 共15道判断题 每题1分 满分15分 大约花费1 2分钟时间 如果你全部答对了 那么恭喜你大神 本篇可能已经满足不了你了 如果结果不理想也不要气馁 读完
  • OpenCV进阶--图像变换(三)

    继上文 五 图像透视变换 首先透视变换是按照物体成像投影规律进行变换 即将物体重新投影到新的成像平面 在透视变换中 透视前的图像和透视后的图像之间的变换关系可以用一个3 3的变换矩阵表示 该矩阵可以通过两幅图像中4个对应点的坐标求取 因此透
  • 【Linux基础】stat函数

    stat函数 函数描述 获取文件属性 函数原型 include
  • iOS开发之数据存取(一)——SQLite

    概览 在iOS开发中数据存储的方式可以归纳为两类 一类是存储为文件 另一类是存储到数据库 例如前面IOS开发系列 Objective C之Foundation框架的文章中提到归档 plist文件存储 包括偏好设置其本质都是存储为文件 只是说