从 SQLite 数据库返回 UITableView 中的 NULL 值

Getting NULL value back in UITableView from SQLite database

程序 运行 没有问题
我可以看到正在打开的数据库
当我向应用程序添加项目时,它在 UITableView

中显示为 NULL

我有测试每个操作的单元测试:创建、更新、删除和计数。
这些都通过了

这是我的单元测试:

- (void)testToDoItemSQLite {
    NSLog(@" ");
    NSLog(@"*** Starting testToDoItemSQLite ***");

    ToDoItemSvcSQLite *todoitemSvc = [[ToDoItemSvcSQLite alloc] init];
    ToDoItem *todoitem = [[ToDoItem alloc] init];
    todoitem.itemname = @"This was created by a unit test";
    [todoitemSvc createToDoItem:todoitem];

    NSMutableArray *todoitems = [todoitemSvc retrieveAllToDoItems];
    NSLog(@"*** number of todoitems: %lu", (unsigned long)todoitems.count);

    todoitem.itemname = @"This was created by a unit test";
    [todoitemSvc updateToDoItem:todoitem];

    [todoitemSvc deleteToDoItem:todoitem];

    NSLog(@"*** Ending testToDoItemSQLite ***");
    NSLog(@" ");
}

这是我的创建检索更新删除 代码

#import "ToDoItemSvcSQLite.h"
#import "sqlite3.h"

@implementation ToDoItemSvcSQLite

NSString *databasePath = nil;
sqlite3 *database = nil;

- (id)init {
    if ((self = [super init])) {
        NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDir = [documentPaths objectAtIndex:0];
        databasePath = [documentsDir stringByAppendingPathComponent:@"todoitem.sqlite3"];

        if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
            NSLog(@"database is open");
            NSLog(@"database file path: %@", databasePath);

            NSString *createSql = @"Create table if not exists todoitem (id integer primary key autoincrement, itemname varchar(200))";

            char *errMsg;
            if (sqlite3_exec(database, [createSql UTF8String], NULL, NULL, &errMsg) !=SQLITE_OK) {
                NSLog(@"Failed to create table %s", errMsg);
            }

        }
        else {
            NSLog(@"*** Failed to open database!");
            NSLog(@"*** SQL error: %s\n", sqlite3_errmsg(database));
        }
    }
    return self;
}

- (ToDoItem *) createToDoItem:(ToDoItem *)todoitem {
    sqlite3_stmt *statement;
    NSString *insertSQL = [NSString stringWithFormat: @"INSERT INTO todoitem (itemname) VALUES (\"%@\")", todoitem.itemname];
    if (sqlite3_prepare(database, [insertSQL UTF8String], -1, &statement, NULL) == SQLITE_OK) {
        if (sqlite3_step(statement) == SQLITE_DONE) {
            todoitem.itemID = sqlite3_last_insert_rowid(database);
            NSLog(@"ToDoItem added");
        }
        else {
            NSLog(@"*** ToDoItem NOT added");
            NSLog(@"*** SQL error: %s\n", sqlite3_errmsg(database));
        }
        sqlite3_finalize(statement);
}

    return todoitem;
}

- (NSMutableArray *) retrieveAllToDoItems {
    NSMutableArray *todoitems = [NSMutableArray array];
    NSString *selectSQL = [NSString stringWithFormat:@"SELECT * FROM todoitem ORDER BY itemname"];
    sqlite3_stmt *statement;
    if (sqlite3_prepare_v2(database, [selectSQL UTF8String], -1, &statement, NULL) == SQLITE_OK) {
        NSLog(@"*** ToDoItems retrieved");
        while (sqlite3_step(statement) == SQLITE_ROW) {
            int id = sqlite3_column_int(statement, 0);
            char *itemnameChars = (char *)sqlite3_column_text(statement, 1);
            ToDoItem *todoitem = [[ToDoItem alloc] init];
            todoitem.itemID = id;
            todoitem.itemname = [[NSString alloc] initWithUTF8String:itemnameChars];
            [todoitems addObject:todoitem];
        }
        sqlite3_finalize(statement);
    }
    else {
        NSLog(@"*** ToDoItems NOT retrieved");
        NSLog(@"*** SQL error %s\n", sqlite3_errmsg(database));

    }
    return todoitems;
}

- (ToDoItem *) updateToDoItem:(ToDoItem *)todoitem {
    NSString *updateSQL = [NSString stringWithFormat: @"UPDATE todoitem SET itemname=\"%@\" WHERE id = %li ", todoitem.itemname, (long)todoitem.itemID];
                           sqlite3_stmt *statement;
                           if (sqlite3_prepare_v2(database, [updateSQL UTF8String], -1, &statement, NULL) == SQLITE_OK) {
                               if (sqlite3_step(statement) == SQLITE_DONE)     {
                                   NSLog(@"*** ToDoItem updated");
                               }
                               else {
                                   NSLog(@"ToDoItem NOT updated");
                                   NSLog(@"*** SQL error: %s\n", sqlite3_errmsg(database));
                               }
                               sqlite3_finalize(statement);
                           }
    return todoitem;
}

- (ToDoItem *) deleteToDoItem:(ToDoItem *)todoitem {
    NSString *deleteSQL = [NSString stringWithFormat: @"DELETE FROM todoitem WHERE id = %i",(int) todoitem.itemID];
    sqlite3_stmt *statement;
    if (sqlite3_prepare_v2(database, [deleteSQL UTF8String], -1, &statement, NULL) == SQLITE_OK) {
        if (sqlite3_step(statement) == SQLITE_DONE) {
            NSLog(@"*** ToDoItem deleted");
        }
        else {
            NSLog(@"*** ToDoItem NOT deleted");
            NSLog(@"*** SQL error: %s\n", sqlite3_errmsg(database));
        }
        sqlite3_finalize(statement);
    }

    return todoitem;
}

- (void)dealloc {
    sqlite3_close(database);
}

@end

这是执行所有操作的代码

添加项目

- (IBAction)addToDoItem:(id)sender {

    [self.view endEditing:YES];

    NSLog(@"saveToDoItem: Adding ToDoItem");
    ToDoItem *todoitem = [[ToDoItem alloc] init];
    todoitem.todoitem = _toDoItem.text;
    [ToDoItemSvc createToDoItem:todoitem];

    //clears text field on save
    _toDoItem.text = @"";

    [self.tableView reloadData];
    NSLog(@"saveToDoItem: todoitem saved");

}

删除项目

- (IBAction)deleteToDoItem:(id)sender {
    NSLog(@"Deleting ToDoItem");

    [self.view endEditing:YES];

}

更新项目

- (IBAction)updateToDoItem:(id)sender {
    NSLog(@"updatingToDoItem: Updating ToDoItem");
    toDoItemObject.todoitem = toDoItem.text;
}

填充 UITableView

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = @"toDoItemCell";
    UITableViewCell *cell =
    [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                  reuseIdentifier:simpleTableIdentifier];
    }
    ToDoItem *toDoItem = [[ToDoItemSvc retrieveAllToDoItems]
                    objectAtIndex:indexPath.row];
    cell.textLabel.text = [toDoItem description];
    return cell;
}

我想弄清楚为什么我 NULL 恢复了我的价值观。
当我查看数据库时,我实际上没有在 table

中看到任何实体

这是否意味着它实际上没有更新?我对此有点困惑。

编辑

我手动更新了数据库。我注意到它没有被阅读。我什至替换了 xcode 显示它正在读取的数据库。还是什么都没有。

当这是 运行 使用存档时,效果很好。显然不同的持久数据方式。

编辑

看起来持久化数据实际上是有效的,但由于某种原因 NULL 被插入到数据库中。我尝试编辑数据库以查看 table 是否会重新加载并获取编辑后的值。它似乎没有用

应用已加载 保存新项目后 查看项目时

todoitem.todoitemtodoitem.itemname有什么区别?您可能正在使用 todoitem.itemname 而不是 todoitem.todoitem:

- (IBAction)addToDoItem:(id)sender {
    …
    todoitem.itemname = _toDoItem.text;
    [ToDoItemSvc createToDoItem:todoitem];
    ...
}

- (IBAction)updateToDoItem:(id)sender {
    NSLog(@"updatingToDoItem: Updating ToDoItem");
    toDoItemObject.itemname = toDoItem.text;
}

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ...
    ToDoItem *toDoItem = [[ToDoItemSvc retrieveAllToDoItems]
                    objectAtIndex:indexPath.row];
    cell.textLabel.text = toDoItem.itemname;
    return cell;
}

希望对您有所帮助!


编辑:

ViewController.m

- (IBAction)addToDoItem:(id)sender {
    [self.view endEditing:YES];

    NSLog(@"saveToDoItem: Adding ToDoItem");
    ToDoItem *todoitem = [[ToDoItem alloc] init];
    todoitem.itemname = _toDoItem.text;  // I edited
    [ToDoItemSvc createToDoItem:todoitem];

    //clears text field on save
    _toDoItem.text = @"";

    [self.tableView reloadData];
    NSLog(@"saveToDoItem: todoitem saved");
}

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *simpleTableIdentifier = @"toDoItemCell";
    UITableViewCell *cell =
    [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:simpleTableIdentifier];
    }
    ToDoItem *toDoItem = [[ToDoItemSvc retrieveAllToDoItems]
                        objectAtIndex:indexPath.row];
    cell.textLabel.text = [toDoItem itemname]; // I edited
    return cell;
}

第二ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    toDoItem.text = toDoItemObject.itemname; // I edited
}

ToDoItemSvcSQLite.m

- (ToDoItem *) updateToDoItem:(ToDoItem *)todoitem {
    NSString *updateSQL = [NSString stringWithFormat: @"UPDATE todoitem SET itemname=\"%@\" WHERE id = %i ", todoitem.itemname, todoitem.id]; // I edited
    sqlite3_stmt *statement;
    if (sqlite3_prepare_v2(database, [updateSQL UTF8String], -1, &statement, NULL) == SQLITE_OK) {
        if (sqlite3_step(statement) == SQLITE_DONE) {
            NSLog(@"*** ToDoItem updated");
        } else {
            NSLog(@"ToDoItem NOT updated");
            NSLog(@"*** SQL error: %s\n", sqlite3_errmsg(database));
        }
        sqlite3_finalize(statement);
    }
    return todoitem;
}

最后,不要在 ToDoItemSvcSQLite 中使用 init,你应该使用 initialize:

// ToDoItemSvcSQLite.m
static sqlite3 *database = nil;

+ (void)initialize {
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDir = [documentPaths objectAtIndex:0];
    databasePath = [documentsDir stringByAppendingPathComponent:@"todoitem"];

    if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
        NSLog(@"database is open");
        NSLog(@"database file path: %@", databasePath);

        NSString *createSql = @"Create table if not exists todoitem (id integer primary key autoincrement, itemname varchar(200))";

        char *errMsg;
        if (sqlite3_exec(database, [createSql UTF8String], NULL, NULL, &errMsg) !=SQLITE_OK) {
            NSLog(@"Failed to create table %s", errMsg);
        }

    } else {
        NSLog(@"*** Failed to open database!");
        NSLog(@"*** SQL error: %s\n", sqlite3_errmsg(database));
    }
}

并在 dealloc:

中注释这一行
- (void)dealloc {
//    sqlite3_close(database);
}

再次编辑:

您似乎更改了 identifier

你应该使用 viewToDoItem:

if ([segue.identifier isEqualToString:@"viewToDoItem"]) {

然后:

// SecondViewController.m
#import "ToDoItemSvcSQLite.h"

- (IBAction)updateToDoItem:(id)sender {
    NSLog(@"updatingToDoItem: Updating ToDoItem");
    toDoItemObject.itemname = toDoItem.text;
    [[ToDoItemSvcSQLite new] updateToDoItem:toDoItemObject];
}