ios - 核心数据似乎获取了空对象

标签 ios objective-c xcode sqlite core-data

在我的iOS应用中,我试图通过核心数据在一个视图中将值保存到sqlite数据库,然后在另一个视图中检索。查看数据库,保存工作正常,对象位于数据库中,符合预期。如果然后转到我的收藏视图,结果将正确显示。但是,如果我很喜欢该应用程序并重新打开,则在检索时,收集视图将显示正确数量的单元格(行),但所有单元格都为空???
提取计数始终返回0,但是已经设置了命令行args以查看sql,这表明提取了正确的行数。我的提取请求如下:

- (NSFetchedResultsController *)fetchedResultsController {

if (_fetchedResultsController != nil) {
    return _fetchedResultsController;
}

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
                               entityForName:@"Player" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];

NSSortDescriptor *sort = [[NSSortDescriptor alloc]
                          initWithKey:@"surname" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

[fetchRequest setFetchBatchSize:20];

NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:context sectionNameKeyPath:nil
                                               cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
NSLog(@"fetch count %@",[NSString stringWithFormat:@"%d",[theFetchedResultsController.fetchedObjects count]]);
_fetchedResultsController.delegate = self;

return _fetchedResultsController;


}

并通过命令行args工具为sql生成以下日志:

CoreData: sql: SELECT 0, t0.Z_PK FROM ZPLAYER t0 ORDER BY t0.ZSURNAME DESC
2015-02-09 23:56:39.522 teamPicker[290:20878] CoreData: annotation: sql    connection fetch time: 0.0013s
2015-02-09 23:56:39.523 teamPicker[290:20878] CoreData: annotation: total   fetch execution time: 0.0020s for 4 rows.
2015-02-09 23:56:39.562 teamPicker[290:20878] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZEMAIL, t0.ZFORENAME, t0.ZMOBNUM, t0.ZMUGSHOT, t0.ZSURNAME, t0.ZTWITTER, t0.ZPROFILE FROM ZPLAYER t0 WHERE  t0.Z_PK IN  (?,?,?,?)  ORDER BY t0.ZSURNAME DESC LIMIT 20
2015-02-09 23:56:39.711 teamPicker[290:20878] CoreData: annotation: sql   connection fetch time: 0.1408s
2015-02-09 23:56:39.711 teamPicker[290:20878] CoreData: annotation: total fetch execution time: 0.1495s for 4 rows.


如您所见,它似乎在运行两个查询,我觉得很奇怪,但是作为菜鸟,我不知道这是否是它的工作方式。它似乎暗示的是,尽管读取计数报告为0,但它已读取了4行,这就是那里的全部内容。

如果有帮助,以下是我的集合视图数据源方法:

#pragma mark - UICollectionView Datasource
// 1
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
id  sectionInfo =
[[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
// 2
- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView {
return 1;
}
// 3
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
Player *info = [_fetchedResultsController objectAtIndexPath:indexPath];
playerPhotoCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"playerCell" forIndexPath:indexPath];
cell.playerImage.image = [UIImage imageWithData:info.mugshot];
cell.playerLabel.text = info.forename;
cell.backgroundColor = [UIColor whiteColor];
return cell;
}


香港专业教育学院可能犯了一些明显的和基本的错误,但如果我能发现它们或找到任何答案,我该死。如果有人可以帮助我指出正确的方向,我将不胜感激。谢谢阅读

编辑:感谢pbasdf指出了我计算调用次数的错误,我将这段代码移到了适当的位置,并且确实返回了正确数量的对象

正如后来承诺的那样,我的playerPhotoCell只是一个脚本,在h文件中具有两个属性,可以充当图像视图的出口,并充当我添加到情节提要中单元格的标签。 .m文件中没有任何方法,因为我认为我的viewController代码可以简单地引用出口并设置它们的值-实际上,如果您在与保存单元格正确填充相同的会话中进行查看,这似乎可以正常工作-关闭并重新加载应用程序时会出现问题-加载了正确数量的单元格,但所有单元格都只有白色背景且没有任何信息

进一步编辑:
如建议的那样,我向cellForItemAtIndexPath添加了一个简单的检查,以查看是否正确填充了这些值,但实际上却是空的。更新的方法如下:

- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    Player *info = [_fetchedResultsController objectAtIndexPath:indexPath];
    playerPhotoCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"playerCell" forIndexPath:indexPath];
    cell.playerImage.image = [UIImage imageWithData:info.mugshot];
    cell.playerLabel.text = info.forename;
    if(info.mugshot){
        NSLog(@"Player Image is of type %@", NSStringFromClass([info.mugshot class]));
    }
    cell.backgroundColor = [UIColor whiteColor];
    return cell;
}


不会调用info.mugshot的NSLog,因为它使条件失败。但是,如果您随后在其中添加新播放器,然后返回同一会话中的viewcontroller视图,则NSLog会报告播放器图像为NSconcreteMutableData类型,并正确填充单元格以及正确数量的空白单元格。如果您关闭该应用程序并关闭然后再返回,它只会返回正确数量的空单元格。

编辑:添加新的播放器代码(警告:潜在危险代码!

- (IBAction)saveBtn:(id)sender {
    self.player = [Player alloc];
    self.player.mugshot = UIImageJPEGRepresentation(_chosenImage, 1);
    self.player.forename = fName.text;
    self.player.surname = sName.text;
    self.player.email = email.text;
    self.player.mobNum = [NSNumber numberWithInteger:[mobNum.text integerValue]];
    if(twitHandle){
        self.player.twitter = twitHandle.text;
    }

    self.profile = [Profile alloc];
    self.profile.position = position.text;
    self.profile.type = _pressedBtnName;
    self.profile.workRate = [NSNumber numberWithInt:(wrRating) ];
    self.profile.stamina = [NSNumber numberWithInteger:(stRating)];
    self.profile.skill = [NSNumber numberWithInteger:(skRating) ];
    self.profile.finishing = [NSNumber numberWithInteger:(fiRating)];
    self.profile.passing = [NSNumber numberWithInteger:(paRating)];
    self.profile.strength = [NSNumber numberWithInteger:(strRating)];

    NSEntityDescription *playerEntityDescription = [NSEntityDescription entityForName:@"Player" inManagedObjectContext:context];
    NSManagedObject *newPlayer = [[NSManagedObject alloc] initWithEntity:playerEntityDescription insertIntoManagedObjectContext:context];
    [newPlayer setValue:self.player.forename forKey:@"forename"];
    [newPlayer setValue:self.player.surname forKey:@"surname"];
    [newPlayer setValue:self.player.email forKey:@"email"];
    [newPlayer setValue:self.player.mobNum forKey:@"mobNum"];
    [newPlayer setValue:self.player.twitter forKey:@"twitter"];
    [newPlayer setValue:self.player.mugshot forKey:@"mugshot"];

    NSEntityDescription *pattrEntityDescription = [NSEntityDescription entityForName:@"Profile" inManagedObjectContext:context];
    NSManagedObject *newPlayerAttr = [[NSManagedObject alloc] initWithEntity:pattrEntityDescription insertIntoManagedObjectContext:context];
    [newPlayerAttr setValue:self.profile.position forKey:@"position"];
    [newPlayerAttr setValue:self.profile.type forKey:@"type"];
    [newPlayerAttr setValue:self.profile.workRate forKey:@"workRate"];
    [newPlayerAttr setValue:self.profile.stamina forKey:@"stamina"];
    [newPlayerAttr setValue:self.profile.skill forKey:@"skill"];
    [newPlayerAttr setValue:self.profile.finishing forKey:@"finishing"];
    [newPlayerAttr setValue:self.profile.passing forKey:@"passing"];
    [newPlayerAttr setValue:self.profile.strength forKey:@"strength"];

    [newPlayer setValue:newPlayerAttr forKey:@"profile"];


在这一点上,我意识到我不需要将逆设置为模型中定义的值,但是如果我没有通过下面的代码行,我将报错-再次很糟糕,但只是想让某些东西起作用

[newPlayerAttr setValue:newPlayer forKey:@"player"];

if (newPlayer.managedObjectContext != nil) {
        NSError *error = nil;
        if (![newPlayer.managedObjectContext save:&error]) {
           // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
           }
}


Player和配置文件对象只是从模型中的相应实体创建的那些类的实例。他们的文件基本上只包含每个Entity属性的一个属性

查询sqlite db表明数据已正确写入

有关信息,我在cellforitematindexpath中放入了一个NSLog来输出[_fetchedResultsController objectAtIndex:indexPath]的类型,然后随播放器一起返回。因此,在此基础上,不知道为什么创建Player实例信息并将其设置为该信息不起作用,但是info的属性为null

编辑:
我已将提取请求添加到我的应用程序委托中,并在didFinishLaunchingWithOptions方法的末尾执行此请求,如下所示:

NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }
    NSLog(@"fetch count %@",[NSString stringWithFormat:@"%lu",(unsigned long)[self.fetchedResultsController.fetchedObjects count]]);
    Player *ken = [self.fetchedResultsController.fetchedObjects firstObject];
    NSLog(@"Surname %@",ken.surname);


提取的对象计数报告6是正确的。但是,尝试将fetchedObjects第一个对象分配给名为Ken的Player实例的下一行在'Surname(null)'的下一行上产生输出,这实际上简直是惊人的*。无论如何,也许我的代码在那里是错误的,但似乎根本没有设置对象,更不用说属性了,因为我还对ken对象进行了测试(未在代码段中显示),例如if(ken){...从来没有遇到过。无论如何都会在枕头上尖叫一会儿,也许会哭一点

*非常烦人

最佳答案

您需要告诉获取的结果控制器实际执行获取。在fetchedResultsController行中的return _fetchedResultsController;代码中插入以下内容:

NSError *error = nil;
if (![_fetchedResultsController performFetch:&error]) {
    NSLog(@"Fetch error %@, %@", error, [error userInfo]);
    abort();
}


编辑

我相信您的问题可能出在保存对象的代码中。如注释中所述,如果不立即使用初始化程序,则不能使用alloc。对于NSManagedObjects(和子类),指定的初始化程序为initWithEntity:insertIntoManagedObjectContext:。但是我想您不希望将self.playerself.profile添加到数据库中,就像newPlayer和newPlayerProfile一样。所以我建议重写如下:

- (IBAction)saveBtn:(id)sender {
    NSEntityDescription *playerEntityDescription = [NSEntityDescription entityForName:@"Player" inManagedObjectContext:context];
    NSManagedObject *newPlayer = [[NSManagedObject alloc] initWithEntity:playerEntityDescription insertIntoManagedObjectContext:context];
    [newPlayer setValue:fName.text forKey:@"forename"];
    [newPlayer setValue:sName.text forKey:@"surname"];
    [newPlayer setValue:email.text forKey:@"email"];
    [newPlayer setValue:[NSNumber numberWithInteger:[mobNum.text integerValue]] forKey:@"mobNum"];
    if(twitHandle){
        [newPlayer setValue:twitHandle.text forKey:@"twitter"];
    }
    [newPlayer setValue:UIImageJPEGRepresentation(_chosenImage, 1) forKey:@"mugshot"];

    NSEntityDescription *pattrEntityDescription = [NSEntityDescription entityForName:@"Profile" inManagedObjectContext:context];
    NSManagedObject *newPlayerAttr = [[NSManagedObject alloc] initWithEntity:pattrEntityDescription insertIntoManagedObjectContext:context];
    [newPlayerAttr setValue:position.text forKey:@"position"];
    [newPlayerAttr setValue:_pressedBtnName forKey:@"type"];
    [newPlayerAttr setValue:[NSNumber numberWithInt:(wrRating)] forKey:@"workRate"];
    [newPlayerAttr setValue:[NSNumber numberWithInteger:(stRating)] forKey:@"stamina"];
    [newPlayerAttr setValue:[NSNumber numberWithInteger:(skRating)] forKey:@"skill"];
    [newPlayerAttr setValue:[NSNumber numberWithInteger:(fiRating)] forKey:@"finishing"];
    [newPlayerAttr setValue:[NSNumber numberWithInteger:(paRating)] forKey:@"passing"];
    [newPlayerAttr setValue:[NSNumber numberWithInteger:(strRating)] forKey:@"strength"];

    [newPlayer setValue:newPlayerAttr forKey:@"profile"];
    // You are right, you shouldn't need the next line
    // [newPlayerAttr setValue:newPlayer forKey:@"player"];

    // Not sure whether self.profile and self.player are used elsewhere,
    // So set them to the newly created objects:
    self.player = newPlayer;
    self.profile = newPlayerAttr;

    if (newPlayer.managedObjectContext != nil) {
        NSError *error = nil;
        if (![newPlayer.managedObjectContext save:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}

关于ios - 核心数据似乎获取了空对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28422074/

相关文章:

ios - 滚动WKWebView时播放声音暂停

ios - 如何使用谓词访问另一个表的数据?

ios - 如何为 post java rest 服务授权用户名和密码

ios - FBRequest问题——获取好友信息

ios - 在 iCloud 上备份文档目录而不同步

swift - 使用 alamofire/multipart 发送图片

swift - 导入到 XCode 的 Collada (.dae) 文件显示没有纹理

ios - Sierra 更新后无法为 iOS 10 编译

iphone - TTThumbsviewcontroller 停止加载缩略图

ios - 为什么当我点击 Objective-C 中的按钮图像时发送者没有被调用?