NSFetchedResultsController numberOfObjects在设备上运行时返回附加(重复)

时间:2011-05-12 07:59:34

标签: iphone ios uitableview nsfetchedresultscontroller

我已经使用NSFetchedResultsController实现了一个UITableView来从sqllite数据库加载数据。我将数据库从设备下载到模拟器,因此它们都是相同的。

我观察到一种奇怪的行为,当在模拟器上运行时,tableview会填充正确数量的单元格(在最初的基本情况下:一个单元格);但是当我在设备上运行相同的代码时,numberOfObjects返回2,tableview显示两个(相同/重复)单元格。

当我检查sqllite文件时,确实只有一个对象/行...

我遵循了我的实现的示例代码,并没有做任何特别的事情。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSInteger count = [[fetchedResultsController sections] count];

    if (count == 0) {
        count = 1;
    }

    return count;
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSInteger numberOfRows = 0;

    if ([[fetchedResultsController sections] count] > 0) {
        id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
        numberOfRows = [sectionInfo numberOfObjects];
    }

    NSLog(@"SwoopListTableViewController::numberOfRowsInSection - numberOfRows:%d", numberOfRows);
    return numberOfRows;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // Dequeue or if necessary create a SwoopTableViewCell, then set its Swoop to the Swoop for the current row.            
    static NSString *SwoopCellIdentifier = @"SwoopCellIdentifier";

    SwoopTableViewCell *SwoopCell = (SwoopTableViewCell *)[tableView dequeueReusableCellWithIdentifier:SwoopCellIdentifier];
    if (SwoopCell == nil) {
        SwoopCell = [[[SwoopTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SwoopCellIdentifier] autorelease];
        SwoopCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }

    [self configureCell:SwoopCell atIndexPath:indexPath];
    return SwoopCell;
}

- (NSFetchedResultsController *)fetchedResultsController {
    // Set up the fetched results controller if needed.
    NSLog(@"SwoopListTableViewController::fetchedResultsController - started");

    if (fetchedResultsController == nil) {

        if (managedObjectContext == nil) 
        { 
            managedObjectContext = [(SwoopAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; 
            NSLog(@"SwoopListTableViewController::fetchedResultsController - set managedObjectContext");
        }

        // Create the fetch request for the entity.
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Swoop" inManagedObjectContext:managedObjectContext];
        [fetchRequest setEntity:entity];

        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:NO];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
        [fetchRequest setSortDescriptors:sortDescriptors];

        NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
        aFetchedResultsController.delegate = self;
        self.fetchedResultsController = aFetchedResultsController;

        [aFetchedResultsController release];
        [fetchRequest release];
        [sortDescriptor release];
        [sortDescriptors release];
    }

    return fetchedResultsController;
}  

我很困惑为什么相同的代码&amp;相同的数据库将在模拟器上显示不同的行为(按预期工作)与设备(3GS - 显示的重复表格单元格)。任何人都可以帮助/解释/提供一些关于我应该看什么的见解吗?

非常感谢, 埃里克

** 编辑1: **我对NSCoreData和NSFetchedResultsController进行了更多调试。似乎fetchRequest确实从managedContext返回了一个重复的对象。这是代码和相应的控制台输出:

控制器代码:

- (void)viewDidLoad {   
    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }       
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:0];
    NSLog(@"SwoopListTableViewController::viewDidLoad - sectionInfo objects:%@", [sectionInfo objects] );        
}

控制台输出:

2011-05-14 17:54:53.388 Swoop[1471:307] SwoopListTableViewController::viewDidLoad - sectionInfo objects:(
    "<Swoop: 0x187d60> (entity: Swoop; id: 0x186480 <x-coredata://A9FF2CC0-77EE-4EFF-A3A6-5F085AA9CCAC/Swoop/p2> ; data: <fault>)",
    "<Swoop: 0x187d60> (entity: Swoop; id: 0x186480 <x-coredata://A9FF2CC0-77EE-4EFF-A3A6-5F085AA9CCAC/Swoop/p2> ; data: <fault>)",
    "<Swoop: 0x188180> (entity: Swoop; id: 0x143a60 <x-coredata://A9FF2CC0-77EE-4EFF-A3A6-5F085AA9CCAC/Swoop/p1> ; data: <fault>)"
)

我觉得奇怪的是,sectionInfo中的前两个对象都具有相同的内存地址“0x187d60”并且都具有相同的x-coredata'路径':“// A9FF2CC0-77EE-4EFF-A3A6-5F085AA9CCAC / Swoop / p2“......有人可以解释这意味着什么,或者可能会发生什么?

谢谢, 埃里克

2 个答案:

答案 0 :(得分:3)

经过更多阅读和深入研究NSCoreData之后,问题似乎是由于设备上如何管理内存的特殊性(我会假设相同的内存约束/管理将应用于模拟器,但我想两者之间存在差异) - 具体而言,如果在低内存警告上卸载视图,则在再次重新加载视图时将执行提取,这会导致可能重复的条目。

我发现以下链接中描述的问题/解决方案解决了我遇到的问题:

Duplicate NSManagedObject with NSFetchedResultsController

另外,通过将此参数传递给应用程序,了解能够启用不同级别的NSCoreData日志记录也很有帮助:

-com.apple.CoreData.SQLDebug 1

在这里阅读更多内容:

从Apple Developer库中,请参阅“核心数据疑难解答:Debugging Fetching

要通过xcode添加参数,请参阅此页面上的“为命令行程序提供启动参数”http://www.meandmark.com/xcodetips.html

答案 1 :(得分:0)

我也一直忙于(SQLite3-)数据库。您是否尝试过清洁设备(使用产品 - &gt;清洁,选择运行iOS设备时)?在构建和运行时,如果存在数据库,它将无法续订......所以,您可能正在使用旧数据库!

<小时/> 程序员往往会忘记的第二件事是你的数据库中的数据库是不可编辑的!首先,您必须将数据库中的数据库(Xcode左侧的项目列表)复制到设备/模拟器上的路径。

NSError *error;
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"database.sqlite3"];
NSString *writableDBPath = [docsDir stringByAppendingPathComponent:@"fdatabase.sqlite3"];
NSFileManager *filemgr = [NSFileManager defaultManager];
BOOL succes = [filemgr fileExistsAtPath:writableDBPath];

if (!succes)
{
    //writable database does not exist, so copy default nonwritable from bundle
    succes = [filemgr copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
    //NSLog(@"Database file copied from bundle to %@", dbPath);
    if (!succes) 
        NSLog(@"Failed to create writable database file with message '%@'.", [error localizedDescription]);
    //return NO;
}

databasePath = [[NSString alloc] initWithFormat:@"%@", writableDBPath];

我在这里创建了两条路径。第一个是你的defaultPath。这是您应该包含在项目中的那个。项目中包含的项目不可编辑,因此我创建了writableDBPath。其余代码在注释中进行了解释,因此它应该是自我解释的,但如果不是,请给我回信。