用于iOS ARC问题的BWDB SQLite包装

我正在尝试使用Bill Weinman的BWDB包装,可以在这里下载: http ://bw.org/iosdata/

我不能把它转换成ARC,可以有更有经验的人看我吗? 主要问题是insertRow&updateRow方法中的va_list,idk如何处理它。 其余的错误很容易摆脱。

提前感谢任何帮助/build议!

头文件

// BWDB.h // Created by Bill Weinman on 2010-09-25. // Copyright 2010 The BearHeart Group, LLC. All rights reserved. #import <Foundation/Foundation.h> #import <sqlite3.h> #define defaultDatabaseFileName @"bwtest.db" #define BWDB_VERSION @"1.0.5" @interface BWDB : NSObject <NSFastEnumeration> { sqlite3 *database; sqlite3_stmt *statement; NSString *tableName; NSString *databaseFileName; NSFileManager *filemanager; // for "fast enumeration" (iterator/generator pattern) NSDictionary * enumRows[1]; // enumerated (iterator) object(s) are passed in a C array // we only ever pass one at a time } @property (nonatomic, retain) NSString *tableName; // object management - (BWDB *) initWithDBFilename: (NSString *) fn; - (BWDB *) initWithDBFilename: (NSString *) fn andTableName: (NSString *) tn; - (void) openDB; - (void) closeDB; - (NSString *) getVersion; - (NSString *) getDBPath; // SQL queries - (NSNumber *) doQuery:(NSString *) query, ...; - (BWDB *) getQuery:(NSString *) query, ...; - (void) prepareQuery:(NSString *) query, ...; - (id) valueFromQuery:(NSString *) query, ...; // CRUD methods - (NSNumber *) insertRow:(NSDictionary *) record; - (void) updateRow:(NSDictionary *) record: (NSNumber *) rowID; - (void) deleteRow:(NSNumber *) rowID; - (NSDictionary *) getRow: (NSNumber *) rowID; - (NSNumber *) countRows; // Raw results - (void) bindSQL:(const char *) cQuery arguments:(va_list)args; - (NSDictionary *) getPreparedRow; - (id) getPreparedValue; // Utilities - (id) columnValue:(int) columnIndex; - (NSNumber *) lastInsertId; @end 

实施文件

 // BWDB.m // Created by Bill Weinman on 2010-09-25. // Copyright 2010 The BearHeart Group, LLC. All rights reserved. #import "BWDB.h" @implementation BWDB @synthesize tableName; #pragma mark - #pragma mark Object Management - (void)dealloc { // NSLog(@"%s", __FUNCTION__); [self closeDB]; [super dealloc]; } // if you're not using the CRUD functions, you don't need a table name - (BWDB *) initWithDBFilename:(NSString *)fn { // NSLog(@"%s", __FUNCTION__); if ((self = [super init])) { databaseFileName = fn; tableName = nil; [self openDB]; } return self; } - (BWDB *) initWithDBFilename: (NSString *) fn andTableName: (NSString *) tn { // NSLog(@"%s", __FUNCTION__); if ((self = [super init])) { databaseFileName = fn; tableName = tn; [self openDB]; } return self; } - (void) openDB { // NSLog(@"%s", __FUNCTION__); if (database) return; filemanager = [[NSFileManager alloc] init]; NSString * dbpath = [self getDBPath]; if (![filemanager fileExistsAtPath:dbpath]) { // try to copy from default, if we have it NSString * defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseFileName]; if ([filemanager fileExistsAtPath:defaultDBPath]) { // NSLog(@"copy default DB"); [filemanager copyItemAtPath:defaultDBPath toPath:dbpath error:NULL]; } } if (sqlite3_open([dbpath UTF8String], &database) != SQLITE_OK) { NSAssert1(0, @"Error: initializeDatabase: could not open database (%s)", sqlite3_errmsg(database)); } [filemanager release]; filemanager = nil; } - (void) closeDB { // NSLog(@"%s", __FUNCTION__); if (database) sqlite3_close(database); if (filemanager) [filemanager release]; database = NULL; filemanager = nil; } - (NSString *) getVersion { return BWDB_VERSION; } - (NSString *) getDBPath { // NSLog(@"%s", __FUNCTION__); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; return [documentsDirectory stringByAppendingPathComponent:databaseFileName]; } // iteration in ObjC is called "fast enumeration" // this is a simple implementation - (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len { if (*enumRows = [self getPreparedRow]) { state->itemsPtr = enumRows; state->state = 0; // not used, customarily set to zero state->mutationsPtr = (unsigned long *) self; // also not used, required by the interface return 1; } else { return 0; } } #pragma mark - #pragma mark SQL Queries // doQuery:query,... // executes a non-select query on the SQLite database // uses SQLbind to bind the variadic parameters // Return value is the number of affect rows - (NSNumber *) doQuery:(NSString *) query, ... { // NSLog(@"%s: %@", __FUNCTION__, query); va_list args; va_start(args, query); const char *cQuery = [query UTF8String]; [self bindSQL:cQuery arguments:args]; if (statement == NULL) return [NSNumber numberWithInt:0]; va_end(args); sqlite3_step(statement); if(sqlite3_finalize(statement) == SQLITE_OK) { return [NSNumber numberWithInt: sqlite3_changes(database)]; } else { NSLog(@"doQuery: sqlite3_finalize failed (%s)", sqlite3_errmsg(database)); return [NSNumber numberWithInt:0]; } } // prepareQuery:query,... // prepares a select query on the SQLite database // uses SQLbind to bind the variadic parameters // use getRow or getValue to get results - (void) prepareQuery:(NSString *) query, ... { // NSLog(@"%s: %@", __FUNCTION__, query); va_list args; va_start(args, query); const char *cQuery = [query UTF8String]; [self bindSQL:cQuery arguments:args]; if (statement == NULL) return; va_end(args); } // prepareQuery:query,... // executes a select query on the SQLite database // uses SQLbind to bind the variadic parameters // Returns NSArray of NSDictionary objects - (BWDB *) getQuery:(NSString *) query, ... { // NSLog(@"%s: %@", __FUNCTION__, query); va_list args; va_start(args, query); const char *cQuery = [query UTF8String]; [self bindSQL:cQuery arguments:args]; if (statement == NULL) return nil; va_end(args); return self; } - (id) valueFromQuery:(NSString *) query, ... { // NSLog(@"%s: %@", __FUNCTION__, query); va_list args; va_start(args, query); const char *cQuery = [query UTF8String]; [self bindSQL:cQuery arguments:args]; if (statement == NULL) return nil; va_end(args); return [self getPreparedValue]; } // bindSQL:arguments // binds variadic arguments to the SQL query. // cQuery is a C string, args is a variadic list of ObjC objects // objects in variadic list are tested for type // see SQLquery for how to call this - (void) bindSQL:(const char *) cQuery arguments:(va_list)args { // NSLog(@"%s: %s", __FUNCTION__, cQuery); int param_count; // preparing the query here allows SQLite to determine // the number of required parameters if (sqlite3_prepare_v2(database, cQuery, -1, &statement, NULL) != SQLITE_OK) { NSLog(@"bindSQL: could not prepare statement (%s)", sqlite3_errmsg(database)); statement = NULL; return; } if ((param_count = sqlite3_bind_parameter_count(statement))) { for (int i = 0; i < param_count; i++) { id o = va_arg(args, id); // determine the type of the argument if (o == nil) { sqlite3_bind_null(statement, i + 1); } else if ([o respondsToSelector:@selector(objCType)]) { if (strchr("islISLB", *[o objCType])) { // integer sqlite3_bind_int(statement, i + 1, [o intValue]); } else if (strchr("fd", *[o objCType])) { // double sqlite3_bind_double(statement, i + 1, [o doubleValue]); } else { // unhandled types NSLog(@"bindSQL: Unhandled objCType: %s", [o objCType]); statement = NULL; return; } } else if ([o respondsToSelector:@selector(UTF8String)]) { // string sqlite3_bind_text(statement, i + 1, [o UTF8String], -1, SQLITE_TRANSIENT); } else { // unhhandled type NSLog(@"bindSQL: Unhandled parameter type: %@", [o class]); statement = NULL; return; } } } va_end(args); return; } #pragma mark - #pragma mark CRUD Methods - (NSNumber *) insertRow:(NSDictionary *) record { // NSLog(@"%s", __FUNCTION__); int dictSize = [record count]; // the values array is used as the argument list for bindSQL id keys[dictSize]; // not used, just a side-effect of getObjects:andKeys id values[dictSize]; [record getObjects:values andKeys:keys]; // convenient for the C array // construct the query NSMutableArray * placeHoldersArray = [NSMutableArray arrayWithCapacity:dictSize]; for (int i = 0; i < dictSize; i++) // array of ? markers for placeholders in query [placeHoldersArray addObject: [NSString stringWithString:@"?"]]; NSString * query = [NSString stringWithFormat:@"insert into %@ (%@) values (%@)", tableName, [[record allKeys] componentsJoinedByString:@","], [placeHoldersArray componentsJoinedByString:@","]]; [self bindSQL:[query UTF8String] arguments:(va_list)values]; sqlite3_step(statement); if(sqlite3_finalize(statement) == SQLITE_OK) { return [self lastInsertId]; } else { NSLog(@"doQuery: sqlite3_finalize failed (%s)", sqlite3_errmsg(database)); return [NSNumber numberWithInt:0]; } } - (void) updateRow:(NSDictionary *) record:(NSNumber *) rowID { // NSLog(@"%s", __FUNCTION__); int dictSize = [record count]; // the values array is used as the argument list for bindSQL id keys[dictSize]; // not used, just a side-effect of getObjects:andKeys id values[dictSize + 1]; [record getObjects:values andKeys:keys]; // convenient for the C array values[dictSize] = rowID; NSString * query = [NSString stringWithFormat:@"update %@ set %@ = ? where id = ?", tableName, [[record allKeys] componentsJoinedByString:@" = ?, "]]; [self bindSQL:[query UTF8String] arguments:(va_list)values]; sqlite3_step(statement); sqlite3_finalize(statement); } - (void) deleteRow:(NSNumber *) rowID { // NSLog(@"%s", __FUNCTION__); NSString * query = [NSString stringWithFormat:@"delete from %@ where id = ?", tableName]; [self doQuery:query, rowID]; } - (NSDictionary *) getRow: (NSNumber *) rowID { NSString * query = [NSString stringWithFormat:@"select * from %@ where id = ?", tableName]; [self prepareQuery:query, rowID]; return [self getPreparedRow]; } - (NSNumber *) countRows { return [self valueFromQuery:[NSString stringWithFormat:@"select count(*) from %@", tableName]]; } #pragma mark - #pragma mark Raw results - (NSDictionary *) getPreparedRow { // NSLog(@"%s", __FUNCTION__); int rc = sqlite3_step(statement); if (rc == SQLITE_DONE) { sqlite3_finalize(statement); return nil; } else if (rc == SQLITE_ROW) { int col_count = sqlite3_column_count(statement); if (col_count >= 1) { NSMutableDictionary * dRow = [NSMutableDictionary dictionaryWithCapacity:1]; for(int i = 0; i < col_count; i++) { // can't use NULL with stringWithUTF8String (bw 1.0.5) const char * sqliteColName = sqlite3_column_name(statement, i); if(sqliteColName) { NSString * columnName = [NSString stringWithUTF8String:sqliteColName]; id o = [self columnValue:i]; if (o != nil) [dRow setObject:o forKey:columnName]; else { NSLog(@"getPreparedRow: columnValue returned nil (%s)", sqlite3_errmsg(database)); return nil; } } else { NSLog(@"getPreparedRow: sqlite3_column_name returned NULL (%s)", sqlite3_errmsg(database)); return nil; } } return dRow; } } else { // rc != SQLITE_ROW NSLog(@"getPreparedRow: could not get row: %s", sqlite3_errmsg(database)); return nil; } return nil; } // returns one value from the first column of the query - (id) getPreparedValue { // NSLog(@"%s", __FUNCTION__); int rc = sqlite3_step(statement); if (rc == SQLITE_DONE) { sqlite3_finalize(statement); return nil; } else if (rc == SQLITE_ROW) { int col_count = sqlite3_column_count(statement); if (col_count < 1) return nil; // shouldn't really ever happen id o = [self columnValue:0]; sqlite3_finalize(statement); return o; } else { // rc == SQLITE_ROW NSLog(@"valueFromPreparedQuery: could not get row: %s", sqlite3_errmsg(database)); return nil; } } #pragma mark - #pragma mark Utility Methods - (id) columnValue:(int) columnIndex { // NSLog(@"%s columnIndex: %d", __FUNCTION__, columnIndex); id o = nil; switch(sqlite3_column_type(statement, columnIndex)) { case SQLITE_INTEGER: o = [NSNumber numberWithInt:sqlite3_column_int(statement, columnIndex)]; break; case SQLITE_FLOAT: o = [NSNumber numberWithFloat:sqlite3_column_double(statement, columnIndex)]; break; case SQLITE_TEXT: o = [NSString stringWithUTF8String:(const char *) sqlite3_column_text(statement, columnIndex)]; break; case SQLITE_BLOB: o = [NSData dataWithBytes:sqlite3_column_blob(statement, columnIndex) length:sqlite3_column_bytes(statement, columnIndex)]; break; case SQLITE_NULL: o = [NSNull null]; break; } return o; } - (NSNumber *) lastInsertId { return [NSNumber numberWithInt: sqlite3_last_insert_rowid(database)]; } @end 

我不知道你是从Lynda培训class还是从其他渠道拿到BWDB的,但是我试了一下,后来又转向了FMDB,另外一个更完整的包装,顺便说一下,已经启用了ARC。 如果你没有与BWDB结婚,你可能想看看FMDB。

如果你想坚持使用BWDB,你可以通过在“Build Phases”中双击BWDB.m文件并在-fno-objc-arc指定它作为非ARC在你的目标设置的“Build Phases”中。 如果你不想要,就不需要转换成ARC。 这种方式通常可以在ARC项目中使用非ARC代码。 请参阅“我可以select退出ARC以查看特定文件吗?” 过渡到ARC常见问题的部分。

在过去几周内,Lynda.com发布了Bill Weinman课程的更新版本,名为iOS SDK和SQLite:构build数据驱动的应用程序

这个新版本解决了ARC问题。 我用ARC编译它,编译得很好。