ios - 从 SQLite 数据库获取 UITableView 中的 NULL 值

标签 ios objective-c cocoa-touch sqlite

程序运行正常
我可以看到正在打开的数据库
当我向应用添加项目时,它在 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
当我查看数据库时,我实际上没有看到表中的任何实体

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

编辑

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

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

编辑

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

this is the app when it first runs this is the tableview after entering an item this is the value when you tape on it what is being seen in the database

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

最佳答案

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;
}

SecondViewController.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);
}

再次编辑:

您似乎更改了标识符: enter image description here enter image description here

你应该使用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];
}

关于ios - 从 SQLite 数据库获取 UITableView 中的 NULL 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30650061/

相关文章:

ios - XMPPFramework - 如何创建群聊室?

ios - "find"如何约束自己?

objective-c - 渲染数学公式

ios - SpriteKit 场景未发布

iphone - objective-c:将日期字符串转换为星期几+月份名称

ios - 如何使用 Swift 在 iOS 中为不同的通知响应播放单独的音频?

ios - 动画 ios 应用程序中的黑色过渡

ios - 查看 Controller /内存管理

objective-c - UITextfield 检查它是否是数字

iphone - 可变高度 UILabel