YYCache icon indicating copy to clipboard operation
YYCache copied to clipboard

修复 Xcode16 编译运行 iOS18 sqlite3_finalize 闪退问题

Open li6185377 opened this issue 1 year ago • 19 comments
trafficstars

修复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 膨胀的治理。

li6185377 avatar Jul 16 '24 09:07 li6185377

觉着这个问题作者会修复么?

zhenghaonagehao avatar Sep 12 '24 03:09 zhenghaonagehao

觉着这个问题作者会修复么?

@zhenghaonagehao li6185377老哥把解决办法贴在这里的初衷也许并不是奢望作者及时的进行修复,而只是给遇到同样问题的朋友们提供一种解决方案,反哺开源社区~

faimin avatar Sep 12 '24 04:09 faimin

能列全一下代码吗?不知道替换哪些代码

kedu avatar Sep 18 '24 07:09 kedu

能列全一下代码吗?不知道替换哪些代码 YYKVStorage.m 104 line

miaoruiyuan avatar Sep 19 '24 01:09 miaoruiyuan

我这104行是这 _dbOpenErrorCount = 0; ,肯定不对

kedu avatar Sep 19 '24 01:09 kedu

我这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 avatar Sep 19 '24 06:09 li6185377

好的,感谢

kedu avatar Sep 19 '24 07:09 kedu

感谢大佬

LoveYao avatar Sep 23 '24 02:09 LoveYao

@li6185377 大佬,替换后会有警告 “Initializing 'sqlite3_stmt *' (aka 'struct sqlite3_stmt *') with an expression of type 'const sqlite3_stmt *' (aka 'const struct sqlite3_stmt *') discards qualifiers”,会有影响吗?

Rogue24 avatar Sep 23 '24 03:09 Rogue24

@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];

huamouChen avatar Oct 22 '24 09:10 huamouChen

这个有办法可以规避吗?除了改代码以外。

Ye-YiChen avatar Nov 08 '24 08:11 Ye-YiChen

pod 导入的 怎么整?

ptlCoder avatar Dec 13 '24 08:12 ptlCoder

pod安装方式:pod 'YYCache_BR'

agiapp avatar Feb 08 '25 11:02 agiapp

使用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;

}

`

jqoo avatar Feb 12 '25 06:02 jqoo

pod安装方式:pod 'YYCache_BR'

大佬 该如何使用 yykit 整体,但 yycache 使用YYCache_BR,如果两个都使用会重复。

Nododo avatar Apr 16 '25 22:04 Nododo

@Nododo 你可以不使用整体引用yykit,单个引用你使用的库,cache单独使用YYCache_BR,YYModel/YYText继续使用原作者的库

Tonsjkjkas avatar May 10 '25 10:05 Tonsjkjkas

在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

JanzenChen avatar May 14 '25 08:05 JanzenChen

使用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

g761007 avatar May 21 '25 03:05 g761007

使用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 啊哈哈

Nododo avatar May 21 '25 03:05 Nododo

line:87 sqlite close failed (21).

ptlCoder avatar Jul 04 '25 03:07 ptlCoder

作者是放弃了开发吗

ptlCoder avatar Jul 04 '25 03:07 ptlCoder