Cách tôi làm điều này là bằng cách xem các cuộn phim. Nếu ngày sửa đổi của tệp SQLite DB trong gói .app gần đây hơn so với tệp trong thư mục tài liệu cục bộ, thì tôi sao chép tệp đó từ gói .app trên ... Đây là mã tôi sử dụng.
sqlite3 *dbh; // Underlying database handle
NSString *name; // Database name (this is the basename part, without the extension)
NSString *pathBundle; // Path to SQLite DB in the .app folder
NSString *pathLocal; // Path to SQLite DB in the documents folder on the device
- (BOOL)automaticallyCopyDatabase { // Automatically copy DB from .app bundle to device document folder if needed
ES_CHECK(!dbh, NO, @"Can't autoCopy an already open DB")
ES_CHECK(name!=nil, NO, @"No DB name specified")
ES_CHECK(pathBundle!=nil, NO, @"No .app bundle path found, this is a cache DB")
ES_CHECK(pathLocal!=nil, NO, @"No local document path found, this is a read-only DB")
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary *localAttr = [fileManager fileAttributesAtPath:pathLocal traverseLink:YES];
BOOL needsCopy = NO;
if (localAttr == nil) {
needsCopy = YES;
} else {
NSDate *localDate;
NSDate *appDBDate;
if (localDate = [localAttr objectForKey:NSFileModificationDate]) {
ES_CHECK([fileManager fileExistsAtPath:pathBundle], NO, @"Internal error: file '%@' does not exist in .app bundle", pathBundle)
NSDictionary *appDBAttr = [fileManager fileAttributesAtPath:pathBundle traverseLink:YES];
ES_CHECK(appDBAttr!=nil, NO, @"Internal error: can't get attributes for '%@'", pathBundle)
appDBDate = [appDBAttr objectForKey:NSFileModificationDate];
ES_CHECK(appDBDate!=nil, NO, @"Internal error: can't get last modification date for '%@'", pathBundle)
needsCopy = [appDBDate compare:localDate] == NSOrderedDescending;
} else {
needsCopy = YES;
}
}
if (needsCopy) {
NSError *error;
BOOL success;
if (localAttr != nil) {
success = [fileManager removeItemAtPath:pathLocal error:&error];
ES_CHECK(success, NO, @"Can't delete file '%@'" ,pathLocal)
}
success = [fileManager copyItemAtPath:pathBundle toPath:pathLocal error:&error];
ES_CHECK(success, NO, @"Can't copy database '%@' to '%@': %@", pathBundle, pathLocal, [error localizedDescription])
ES_TRACE(@"Copied DB '%@' to '%@'", pathBundle, pathLocal)
return success;
}
return YES;
}
Các ES_CHECK thứ chỉ là macro mà mở rộng ra không có gì trong chế độ phát hành, và nâng cao một ngoại lệ trong chế độ gỡ lỗi ... Họ trông như thế này:
#if ES_DEBUG
#define ES_ASSERT(cond) assert(cond);
#define ES_LOG(msg...) NSLog(msg);
#define ES_TRACE(msg...) NSLog(msg);
#else
#define ES_ASSERT(cond)
#define ES_LOG(msg...)
#define ES_TRACE(msg...)
#endif
#define ES_CHECK(cond, ret, msg...) if (!(cond)) { ES_LOG(msg) ES_ASSERT(cond) return (ret); } // Check with specified return value (when condition fails)
Macro 'ES_CHECK' là thú vị ... Trong chế độ gỡ lỗi, bạn thất bại với xác nhận, nhưng trong chế độ phát hành, bạn trả lại mã lỗi, vì vậy mã thực tế hoạt động cực kỳ khác nhau trong chế độ gỡ lỗi và phát hành. Vì vậy, bạn chỉ có thể kiểm tra xem mã gọi có thể xử lý mã lỗi được trả về bằng cách chuyển tất cả gỡ lỗi không? –
Có. Nhưng không ai trong số các kiểm tra thất bại, đó là ý tưởng. Nếu họ đã từng thất bại trong sản xuất, họ cắt ngắn thường xuyên bằng cách trả lại một giá trị hợp lý thay vì bị rơi ... –