YYCache
YYCache copied to clipboard
修复 Xcode16 编译运行 iOS18 sqlite3_finalize 闪退问题
修复Xcode16编译引起的闪退,在 iOS18 中,需要提前对 sqlite3_stmt 执行 sqlite3_finalize
@implementation YYKVStorage
- (BOOL)_dbClose {
...
// 原代码
if (_dbStmtCache) CFRelease(_dbStmtCache);
// 替换为
if (_dbStmtCache) {
CFIndex size = CFDictionaryGetCount(_dbStmtCache);
CFTypeRef *valuesRef = (CFTypeRef *)malloc(size * sizeof(CFTypeRef));
CFDictionaryGetKeysAndValues(_dbStmtCache, NULL, (const void **)valuesRef);
const sqlite3_stmt **stmts = (const sqlite3_stmt **)valuesRef;
for (CFIndex i = 0; i < size; i ++) {
sqlite3_stmt *stmt = (sqlite3_stmt *)stmts[i];
sqlite3_finalize(stmt);
}
free(valuesRef);
CFRelease(_dbStmtCache);
}
...
}
@end
建议改用 CFDictionaryApplyFunction 更加优雅: https://github.com/ibireme/YYCache/pull/163
该 pull request 还有对 WAL 膨胀的治理。
觉着这个问题作者会修复么?
觉着这个问题作者会修复么?
@zhenghaonagehao li6185377老哥把解决办法贴在这里的初衷也许并不是奢望作者及时的进行修复,而只是给遇到同样问题的朋友们提供一种解决方案,反哺开源社区~
能列全一下代码吗?不知道替换哪些代码
能列全一下代码吗?不知道替换哪些代码 YYKVStorage.m 104 line
我这104行是这 _dbOpenErrorCount = 0; ,肯定不对
我这104行是这 _dbOpenErrorCount = 0; ,肯定不对
YYKVStorage.m
// 原代码
if (_dbStmtCache) CFRelease(_dbStmtCache);
// 替换为
if (_dbStmtCache) {
CFIndex size = CFDictionaryGetCount(_dbStmtCache);
CFTypeRef *valuesRef = (CFTypeRef *)malloc(size * sizeof(CFTypeRef));
CFDictionaryGetKeysAndValues(_dbStmtCache, NULL, (const void **)valuesRef);
const sqlite3_stmt **stmts = (const sqlite3_stmt **)valuesRef;
for (CFIndex i = 0; i < size; i ++) {
sqlite3_stmt *stmt = stmts[i];
sqlite3_finalize(stmt);
}
free(valuesRef);
CFRelease(_dbStmtCache);
}
好的,感谢
感谢大佬
@li6185377 大佬,替换后会有警告 “Initializing 'sqlite3_stmt *' (aka 'struct sqlite3_stmt *') with an expression of type 'const sqlite3_stmt *' (aka 'const struct sqlite3_stmt *') discards qualifiers”,会有影响吗?
@li6185377 大佬,替换后会有警告 “Initializing 'sqlite3_stmt *' (aka 'struct sqlite3_stmt *') with an expression of type 'const sqlite3_stmt *' (aka 'const struct sqlite3_stmt *') discards qualifiers”,会有影响吗?
sqlite3_stmt *stmt = (sqlite3_stmt *)stmts[i];
这个有办法可以规避吗?除了改代码以外。
pod 导入的 怎么整?
pod安装方式:pod 'YYCache_BR'
使用sqlite3_close_v2应该会更好些:
`
- (BOOL)_dbClose { if (!_db) return YES;
int result = 0;
if (_dbStmtCache) CFRelease(_dbStmtCache);
_dbStmtCache = NULL;
result = sqlite3_close_v2(_db);
if (result != SQLITE_OK) {
if (_errorLogsEnabled) {
NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result);
}
}
_db = NULL;
return YES;
}
`
pod安装方式:pod 'YYCache_BR'
大佬 该如何使用 yykit 整体,但 yycache 使用YYCache_BR,如果两个都使用会重复。
@Nododo 你可以不使用整体引用yykit,单个引用你使用的库,cache单独使用YYCache_BR,YYModel/YYText继续使用原作者的库
在Podfile中修改文件中的代码
post_install do |installer|
##Fix YYcache crash Bug -- before pod install,you must update Permission system('chmod 644 Pods/YYCache/YYCache/YYKVStorage.m')
find_and_replace("Pods/YYCache/YYCache/YYKVStorage.m", "#pragma mark - db", "#pragma mark - fix_db \n static void _finalizeStatement(const void *key, const void *value, void *context) { sqlite3_finalize((sqlite3_stmt *)value); }"); find_and_replace("Pods/YYCache/YYCache/YYKVStorage.m", "if (_dbStmtCache) CFRelease(_dbStmtCache);", "if (_dbStmtCache) {CFDictionaryApplyFunction(_dbStmtCache, _finalizeStatement, NULL);CFRelease(_dbStmtCache);}");
end
def find_and_replace(dir, findstr, replacestr) Dir[dir].each do |name| text = File.read(name) replace = text.gsub(findstr,replacestr) if text != replace puts "Fix: " + name File.open(name, "w") { |file| file.puts replace } STDOUT.flush end end Dir[dir + '*/'].each(&method(:find_and_replace)) end
使用category的方式,直接新增檔案到自己專案
//
// YYKVStorage+IOS18.h
//
@interface YYKVStorage (IOS18)
@end
//
// YYKVStorage+IOS18.m
//
#import <time.h>
#if __has_include(<sqlite3.h>)
#import <sqlite3.h>
#else
#import "sqlite3.h"
#endif
#import "YYKVStorage+IOS18.h"
@implementation YYKVStorage (IOS18)
+ (void)load {
if (@available(iOS 18, *)) {
Method original, swizzled;
original = class_getInstanceMethod(objc_getClass("YYKVStorage"), NSSelectorFromString(@"_dbClose"));
swizzled = class_getInstanceMethod(self, @selector(swizzle_dbClose));
method_exchangeImplementations(original, swizzled);
}
}
- (BOOL)swizzle_dbClose {
// _db
sqlite3 *_db = ({
Ivar ivar = class_getInstanceVariable([self class], "_db");
ptrdiff_t offset = ivar_getOffset(ivar);
unsigned char* bytes = (unsigned char *)(__bridge void*)self;
sqlite3 *ptr = (sqlite3 *)(bytes+offset);
ptr;
});
CFMutableDictionaryRef *dbStmtCache = ({
Ivar ivar = class_getInstanceVariable([self class], "_dbStmtCache");
ptrdiff_t offset = ivar_getOffset(ivar);
unsigned char* bytes = (unsigned char *)(__bridge void*)self;
CFMutableDictionaryRef *ptr = (CFMutableDictionaryRef *)(bytes+offset);
ptr;
});
NSUInteger *errorLogsEnabled = ({
Ivar ivar = class_getInstanceVariable([self class], "_errorLogsEnabled");
ptrdiff_t offset = ivar_getOffset(ivar);
unsigned char* bytes = (unsigned char *)(__bridge void*)self;
NSUInteger *ptr = (NSUInteger *)(bytes+offset);
ptr;
});
if (!_db) return YES;
int result = 0;
BOOL retry = NO;
BOOL stmtFinalized = NO;
if (*dbStmtCache) {
CFIndex size = CFDictionaryGetCount(*dbStmtCache);
CFTypeRef *valuesRef = (CFTypeRef *)malloc(size * sizeof(CFTypeRef));
CFDictionaryGetKeysAndValues(*dbStmtCache, NULL, (const void **)valuesRef);
const sqlite3_stmt **stmts = (const sqlite3_stmt **)valuesRef;
for (CFIndex i = 0; i < size; i ++) {
sqlite3_stmt *stmt = (sqlite3_stmt *)stmts[i];
sqlite3_finalize(stmt);
}
free(valuesRef);
CFRelease(*dbStmtCache);
}
*dbStmtCache = NULL;
do {
retry = NO;
result = sqlite3_close(_db);
if (result == SQLITE_BUSY || result == SQLITE_LOCKED) {
if (!stmtFinalized) {
stmtFinalized = YES;
sqlite3_stmt *stmt;
while ((stmt = sqlite3_next_stmt(_db, nil)) != 0) {
sqlite3_finalize(stmt);
retry = YES;
}
}
} else if (result != SQLITE_OK) {
if (*errorLogsEnabled) {
NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result);
}
}
} while (retry);
_db = NULL;
return YES;
}
@end
使用category的方式,直接新增檔案到自己專案
// // YYKVStorage+IOS18.h // @interface YYKVStorage (IOS18) @end// // YYKVStorage+IOS18.m // #import <time.h> #if __has_include(<sqlite3.h>) #import <sqlite3.h> #else #import "sqlite3.h" #endif #import "YYKVStorage+IOS18.h" @implementation YYKVStorage (IOS18) + (void)load { Method original, swizzled; original = class_getInstanceMethod(objc_getClass("YYKVStorage"), NSSelectorFromString(@"_dbClose")); swizzled = class_getInstanceMethod(self, @selector(swizzle_dbClose)); method_exchangeImplementations(original, swizzled); } - (BOOL)swizzle_dbClose { // _db sqlite3 *_db = ({ Ivar ivar = class_getInstanceVariable([self class], "_db"); ptrdiff_t offset = ivar_getOffset(ivar); unsigned char* bytes = (unsigned char *)(__bridge void*)self; sqlite3 *ptr = (sqlite3 *)(bytes+offset); ptr; }); CFMutableDictionaryRef *dbStmtCache = ({ Ivar ivar = class_getInstanceVariable([self class], "_dbStmtCache"); ptrdiff_t offset = ivar_getOffset(ivar); unsigned char* bytes = (unsigned char *)(__bridge void*)self; CFMutableDictionaryRef *ptr = (CFMutableDictionaryRef *)(bytes+offset); ptr; }); NSUInteger *errorLogsEnabled = ({ Ivar ivar = class_getInstanceVariable([self class], "_errorLogsEnabled"); ptrdiff_t offset = ivar_getOffset(ivar); unsigned char* bytes = (unsigned char *)(__bridge void*)self; NSUInteger *ptr = (NSUInteger *)(bytes+offset); ptr; }); if (!_db) return YES; int result = 0; BOOL retry = NO; BOOL stmtFinalized = NO; if (*dbStmtCache) { CFIndex size = CFDictionaryGetCount(*dbStmtCache); CFTypeRef *valuesRef = (CFTypeRef *)malloc(size * sizeof(CFTypeRef)); CFDictionaryGetKeysAndValues(*dbStmtCache, NULL, (const void **)valuesRef); const sqlite3_stmt **stmts = (const sqlite3_stmt **)valuesRef; for (CFIndex i = 0; i < size; i ++) { sqlite3_stmt *stmt = (sqlite3_stmt *)stmts[i]; sqlite3_finalize(stmt); } free(valuesRef); CFRelease(*dbStmtCache); } *dbStmtCache = NULL; do { retry = NO; result = sqlite3_close(_db); if (result == SQLITE_BUSY || result == SQLITE_LOCKED) { if (!stmtFinalized) { stmtFinalized = YES; sqlite3_stmt *stmt; while ((stmt = sqlite3_next_stmt(_db, nil)) != 0) { sqlite3_finalize(stmt); retry = YES; } } } else if (result != SQLITE_OK) { if (*errorLogsEnabled) { NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result); } } } while (retry); _db = NULL; return YES; } @end
good idea 啊哈哈
line:87 sqlite close failed (21).
作者是放弃了开发吗