WCDB当设置了setConfig后会导致强引用不释放的问题九)
前言
由于项目性质为IM,数据使用
sqlite
+wcdb
,需要本地数据库管理大量数据,为了性能及查询方便,采用的是每个群都是独立的库,在数据读写时都需要初始化独立的database
,当database
过多时就需要及时释放,= 在中间进行性能优化时,给每个database
设置了setConfig
,导致同一个路径的database
一直不释放,知道再次创建相同路径的database
才会释放,当有大量的单聊、群聊消息时,创建了大量的database
又没法及时释放,内存使用过高,导致App被系统杀掉
导致database被循环引用的代码
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
class StudentDBWorker {
let dbPath = PathTool.documentsDir.appendPath("/xx/xx/xx.db")
deinit {
print("deinit--StudentDBWorker-\(self)")
}
init() {
print(dbPath)
let db = Database(at: "/xxx/xx/xx.db")
do {
// 只要设置了这个setConfig就会导致被强引用不能释放,如果没有这一段,执行完初始化就会销毁db
db.setConfig(named: "checkSizeBeforeProcessDemo", withInvocation: { handle in
try handle.exec(StatementPragma().pragma(.journalSizeLimit).to(1024*8))
}, withPriority: Database.ConfigPriority.default)
// 要初始化表
try db.create(table: StudentDBModel.tableName, of: StudentDBModel.self)
} catch {
print(error)
}
}
@discardableResult
func getStuCount() -> Int? {
// 这里重新创建相同路径的database时,会触发之前相同路径的database销毁
let db = Database(at: "/xxx/xx/xx.db")
do {
// 没有on就是全量字段插入
let val = try db.getValue(on: StudentDBModel.Properties.id.count(), fromTable: StudentDBModel.tableName)
return val.intValue
} catch {
print(error)
return nil
}
}
}
产生这个的原因时WCDB
的func setConfig(named name: String, withInvocation invocation: @escaping Config, withUninvocation uninvocation: Config? = nil, withPriority priority: ConfigPriority = ConfigPriority.default)
里代码强引用导致Database.swift
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
func setConfig(named name: String,
withInvocation invocation: @escaping Config,
withUninvocation uninvocation: Config? = nil,
withPriority priority: ConfigPriority = ConfigPriority.default) {
let cppInvocation: @convention(c) (UnsafeMutableRawPointer, CPPHandle) -> Bool = {
cppContext, cppHandle in
let invocationWrap: ValueWrap<(CPPHandle) -> Bool>? = ObjectBridge.extractTypedSwiftObject(cppContext)
guard let invocationWrap = invocationWrap else {
return false
}
return invocationWrap.value(cppHandle)
}
// let invocationBlock: (CPPHandle) -> Bool = { [weak self]
// cppHandle in
// guard let self = self else {
// return false
// }
// let handle = Handle(withCPPHandle: cppHandle, database: self)
// var ret = true
// do {
// try invocation(handle)
// } catch {
// ret = false
// }
// return ret
// }
// 这里强引用了self,导致不能释放database
let invocationBlock: (CPPHandle) -> Bool = {
cppHandle in
let handle = Handle(withCPPHandle: cppHandle, database: self)
var ret = true
do {
try invocation(handle)
} catch {
ret = false
}
return ret
}
let invocationWrap = ValueWrap(invocationBlock)
let invocationWrapPointer = ObjectBridge.getUntypeSwiftObject(invocationWrap)
var uninvocationWrapPointer: UnsafeMutableRawPointer?
if let uninvocation = uninvocation {
let uninvocationBlock: (CPPHandle) -> Bool = { [weak self]
cppHandle in
guard let self = self else {
return false
}
let handle = Handle(withCPPHandle: cppHandle, database: self)
var ret = true
do {
try uninvocation(handle)
} catch {
ret = false
}
return ret
}
let uninvocationWrap = ValueWrap(uninvocationBlock)
uninvocationWrapPointer = ObjectBridge.getUntypeSwiftObject(uninvocationWrap)
}
WCDBDatabaseConfig(database,
name.cString,
cppInvocation,
invocationWrapPointer,
uninvocationWrapPointer != nil ? cppInvocation : nil,
uninvocationWrapPointer,
priority.rawValue,
ObjectBridge.objectDestructor)
}
解决方案
修改源码把相关强用引用的self
改成弱引用
另外在使用setConfig
来处理journal_size_limit
处理在性能上是否有影响还没测过
该问题是一个大佬发现定位到的,从解决实际问题的效率速度来来说,真心佩服,挺厉害
This post is licensed under CC BY 4.0 by the author.