gcoder.io

记录此时此刻


  • 首页

  • 归档

  • 标签

  • 关于

  • 搜索

iOS 数据库模块搭建方案

发表于 2015-07-19 | 分类于 iOS | 阅读次数
字数统计 1,398

数据库作为App缓存设计的首选,存在一些开发的陷阱,同时需要考虑性能、开发效率和可维护性,笔者建议自行搭建数据库管理类,同时配合成熟的开源ORM框架快速搭建数据库模块。
本示例采用fmdb框架 https://github.com/ccgus/fmdb

SQLite多线程访问问题分析:

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
- (void) testfmdb{
// db path
NSString * cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
NSString * dbPath = [cachePath stringByAppendingPathComponent:@"test.sqlite"];
// create db
_db = [FMDatabase databaseWithPath:dbPath];
// open db
BOOL openDbResult = [_db open];
NSLog(@"openDbResult===%@", openDbResult ? @"YES":@"NO");
// create table
BOOL createTableResult = [_db executeUpdate:@"create table if not exists user(id integer primary key autoincrement, name text, age integer)"];
NSLog(@"createTableResult===%@", createTableResult ? @"YES":@"NO");
// test multithreading
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue setMaxConcurrentOperationCount:10];
for (int i = 0; i < 100; i ++) {
NSBlockOperation *opBlock = [NSBlockOperation blockOperationWithBlock:^{
[self insert];
[self query];
}];
[myQueue addOperation:opBlock];
}
}
-(void) insert{
for (int i = 0; i < 100; i ++) {
NSString *name = [NSString stringWithFormat:@"name_%d",i];
NSString *age = [NSString stringWithFormat:@"%d",i];
[_db executeUpdate:@"insert into user(name,age) values (?, ?)", name,age];
}
}
-(void) query{
FMResultSet * set = [_db executeQuery:@"select * from user"];
while ([set next]) {
int id = [set intForColumn:@"id"];
NSString *name = [set stringForColumn:@"name"];
int age = [set intForColumn:@"age"];
NSLog(@"%d===%@===%d", id, name, age);
}
}
  • 调用testfmdb方法抛出异常:The FMDatabase is currently in use.
  • ios中SQLite同Android中SQLite一样,数据库不支持多线程读写并发访问,Android底层对SQLite单个数据库连接读写操作做了同步处理,也仅能支持单数据库连接的并发访问。
阅读全文 »

iOS 多线程

发表于 2015-06-12 | 分类于 iOS | 阅读次数
字数统计 498

NSThread

  • 使用NSThread对象建立一个线程非常方便,但要使用NSThread管理多个线程较困难,不推荐使用;
  • [NSThread currentThread]跟踪任务所在线程,适用于这三种技术。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void) testNSThread{
// 创建方法1
[NSThread detachNewThreadSelector:@selector(actionForNSThread:) toTarget:self withObject:@"1"];
// 创建方法2
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(actionForNSThread:) object:@"1"];
[thread start];
}
- (void) actionForNSThread:(NSString *)extraData{
@autoreleasepool{
NSLog(@"actionForNSThread===%@", extraData);
}
}

NSObject

NSObject的多线程方法:
  • 开启后台执行任务的方法:
1
- (void)performSelectorInBackground:(SEL)@Selector withObject:(id)arg
  • 在后台线程中通知主线程执行任务的方法:
1
- (void)performSelectorOnMainThread:(SEL)@Selector withObject:(id)arg waitUntilDone:(BOOL)wait
NSObject的多线程方法注意事项:
  • NSObject的多线程方法使用的是NSThread的多线程技术.
  • NSThread的多线程技术不会自动使用@autoreleasepool.
    在使用NSObject或NSThread的多线程技术时,如果涉及到对象分配,需要手动添加@autoreleasepool.
阅读全文 »

iOS 数据存储方式

发表于 2015-06-09 | 分类于 iOS | 阅读次数
字数统计 791

Plist(NSArray/NSDictionary):

  • 支持的数据类型有NSString、 NSNumber、NSDate、 NSArray、NSDictionary、BOOL、NSInteger、NSFloat等系统定义的数据类型,底层是基于key-value的NSDictionary。
  • 项目中预置的plist文件只能读取不支持修改、删除;
  • 运行期创建的plist文件支持读取、新写入、修改、删除等操作,写入时必须是完整的dic,不支持增量写入方式。
读取项目中预置plist文件:
1
2
3
4
5
- (void) testPlist{
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"plist"];
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
NSLog(@"data===%@", data);
}
运行期创建的plist文件读取数据:
1
2
3
4
5
6
7
8
9
- (void) testReadPlist{
// 存储路径
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *plistPath = [paths objectAtIndex:0];
NSString *filename=[plistPath stringByAppendingPathComponent:@"test_new.plist"];
// 查询
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile:filename];
NSLog(@"%@", data);
}
运行期创建新plist并写入数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void) testWriteNewPlist{
// 存储路径
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *plistPath = [paths objectAtIndex:0];
NSString *filename=[plistPath stringByAppendingPathComponent:@"test_new.plist"];
// 原始数据
NSMutableDictionary *data = [NSMutableDictionary dictionary];
// key存在value被覆盖,key不存在新增
[data setObject:@(1) forKey:@"age"];
// 写入数据
BOOL success = [data writeToFile:filename atomically:YES];
NSLog(@"success===%@", success?@"YES":@"NO");
// 查询验证结果
NSMutableDictionary *data1 = [[NSMutableDictionary alloc] initWithContentsOfFile:filename];
NSLog(@"%@", data1);
}
运行期创建的plist修改、删除数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (void) testModifyAndRemovePlist{
// 存储路径
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *plistPath = [paths objectAtIndex:0];
NSString *filename=[plistPath stringByAppendingPathComponent:@"test_new.plist"];
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile:filename];
NSLog(@"%@", data);
// 修改数据
[data setObject:@(2) forKey:@"age"];
// 删除数据
[data removeObjectForKey:@"name"];
// 写入数据
BOOL success = [data writeToFile:filename atomically:YES];
NSLog(@"success===%@", success?@"YES":@"NO");
// 查询验证结果
NSMutableDictionary *data1 = [[NSMutableDictionary alloc] initWithContentsOfFile:filename];
NSLog(@"%@", data1);
}
阅读全文 »

Android 数据库模块搭建方案

发表于 2015-05-02 | 分类于 Android | 阅读次数
字数统计 960

数据库作为App缓存设计的首选,存在一些开发的陷阱,同时需要考虑性能、开发效率和可维护性,笔者建议自行搭建数据库管理类,同时配合成熟的开源ORM框架快速搭建数据库模块。
本方案采用ORM框架greenDAO,greenDAO是成熟的ORM框架,在性能和内存占用上很出色
https://github.com/greenrobot/greenDAO

Android数据库存在的问题:

  1. SQLite是数据库级别锁,同一个数据库连接可以多线程操作,同时读、写、读写操作,SQLite底层做了同步处理;
  2. 多个数据库连接,只可以多线程读操作,不可写或读写操作,否则会抛出锁表异常
    android.database.sqlite.SQLiteDatabaseLockedException: database is locked
    总结:多个数据库连接不能同时操作涉及到写的操作,多线程写操作必须保证一个数据库连接。

解决方案:

1. 同步锁方案:

考虑性能,建议使用ReadWriteLock,ReadWriteLock可以并发读,可以大幅减少读操作造成的锁开销;

  • 在Application onCreate方法中创建全局唯一的ReadWriteLock;

    1
    public static ReadWriteLock lock = new ReentrantReadWriteLock(false);
  • 在DAO中调用Application的ReadWriteLock,

  • 读操作前调用lock.readLock().lock();加锁,执行完毕调后调用
    lock.readLock.unlock();解锁;
  • 写操作调用lock.writeLock().lock(); 加锁,执行完毕后调用
    lock.writeLock().unlock(); 解锁;
  • 在每次读写操作前需要获取数据库连接,用完后关闭释放资源。
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
public class UserInfoDAO {
private Context context;
private ReadWriteLock lock;
public TestDAO(Context context, ReadWriteLock lock){
this.context = context;
this.lock = lock;
}
public SQLiteDatabase getConnection() {
SQLiteDatabase sqliteDatabase = null;
try {
sqliteDatabase = new DatabaseHelper(context).getWritableDatabase();
} catch (Exception e) {
}
return sqliteDatabase;
}
public void insert(UserInfo userInfo){
lock.writeLock().lock();
SQLiteDatabase db = null;
try {
ContentValues values = new ContentValues();
values.put("name", userInfo.getName());
values.put("age", userInfo.getAge);
db = getConnection();
if(db != null && db.isOpen()){
db.insert("userInfo", null, values);
}
} catch (Exception e) {
}finally{
if (db != null && db.isOpen()) {
db.close();
}
myLock.writeLock().unlock();
}
}
public List<UserInfo> query(){
List<UserInfo> list = new ArrayList<UserInfo>();
lock.readLock().lock();
SQLiteDatabase db = null;
try {
db = getConnection();
if(db != null && db.isOpen()){
Cursor c = db.rawQuery("SELECT * FROM userInfo", null);
UserInfo info;
while (c.moveToNext()) {
info = new UserInfo();
info.setName(c.getString(c.getColumnIndex("name")));
info.setAge(c.getInt(c.getColumnIndex("age")));
list.add(info);
}
}
} catch (Exception e) {
}finally{
if (db != null && db.isOpen()) {
db.close();
}
lock.readLock().unlock();
}
return list;
}
}
阅读全文 »
12
gcoder.io

gcoder.io

14 日志
5 分类
26 标签
GitHub Weibo
© 2015 - 2017 gcoder.io
由 Hexo 强力驱动
主题 - NexT.Pisces
0%