核心数据。对大尺寸 NSManagedObject 的 NSMutableSet 进行排序

Posted

技术标签:

【中文标题】核心数据。对大尺寸 NSManagedObject 的 NSMutableSet 进行排序【英文标题】:Core Data. Sort NSMutableSet of big size NSManagedObject 【发布时间】:2012-06-06 11:20:36 【问题描述】:

我想获取大约 200 个 NSManagedObject,每个大小约为 5Mb。如果我使用 executeFetchRequest,我会收到内存警告和应用程序崩溃(因为获取的 NSManagedObjects 出现在 iPhone 的 RAM 中)。如果我可以使用 executeFetchRequest,我可以使用 sortdescriptor 按 id 对数据进行排序。但我不能(或者可能我应该为 NSFetchRequest 设置适当的属性,但我不知道是什么属性)。 这就是为什么我使用另一种方法来加载大量数据: 我有实体相册,它有很多实体SpecificImage。我使用这段代码来获取当前专辑的所有图片:

- (NSMutableSet *)getAllSpecificImages

        NSString *entityName = kEntityName;
        AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context = appDelegate.managedObjectContext;
        NSEntityDescription *entityDesctiption = [NSEntityDescription 
                                                  entityForName: entityName
                                                  inManagedObjectContext:context];
        // check if town exists
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id == %d", self.CurrentCategoryItemID];
        NSFetchRequest *requestToCheckExistense = [[NSFetchRequest alloc] init];
        [requestToCheckExistense setEntity:entityDesctiption];
        [requestToCheckExistense setPredicate:predicate];
        NSArray *objects = [context executeFetchRequest:requestToCheckExistense error:nil];
        [requestToCheckExistense release];
        if (objects == nil)
        
            NSLog(@"there was an error");
            return nil;
        
        NSManagedObject *object;
        if ([objects count] > 0)
        
            // edit item
            object = [objects objectAtIndex:0];

        
        else
        
            // if object doesn't exist, find max id to imlement autoincrement
            NSFetchRequest *request = [[NSFetchRequest alloc] init];
            [request setEntity:entityDesctiption];
            NSArray *allobjects = [context executeFetchRequest:request error:nil];
            [request release];
            NSInteger newID = 1;
            if ([allobjects count] > 0)
            
                NSNumber *maxID = [allobjects valueForKeyPath:@"@max.id"];
                newID = [maxID intValue] + 1;
            

            // write item
            object = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context]; 
            [object setValue:[NSNumber numberWithInt:newID] forKey:@"id"];
            self.CurrentCategoryItemID = newID;
        

    NSMutableSet *set = [object mutableSetValueForKey:@"specificImages"];
    return set;

然后我得到 NSManagedObjects 数组

NSMutableArray *array = [NSMutableArray arrayWithArray:[setOfManagedObjectsSpecificImages allObjects]];

但随后我尝试获取此 NSManagedObjects 的排序数组,但我再次收到内存警告和应用程序崩溃。 我尝试使用

NSSortDescriptor *sortDiscriptor = [NSSortDescriptor sortDescriptorWithKey:@"id" ascending:YES];
[array sortUsingDescriptors:[NSArray arrayWithObject:sortDiscriptor]];

[self quickSort:array left:0 right:([array count] - 1)];

在哪里

- (void)quickSort: (NSMutableArray *)arr left:(NSInteger)left right:(NSInteger) right

    @autoreleasepool 
        int i = left, j = right;

        int pivot = [((SpecificImage *)[arr objectAtIndex:(left + right) / 2]).id intValue];



        /* partition */

        while (i <= j) 
            while ([((SpecificImage *)[arr objectAtIndex:i]).id intValue] < pivot)
            

                i++;
            

            while ([((SpecificImage *)[arr objectAtIndex:j]).id intValue] > pivot)
            

                j--;
            

            if (i <= j) 

                [arr exchangeObjectAtIndex:i withObjectAtIndex:j];

                i++;

                j--;

            

        ;


        /* recursion */

        if (left < j)
        
            [self quickSort:arr left:left right:j];
        

        if (i < right)
        
            [self quickSort:arr left:i right:right];
        

    


但这并没有帮助。

从我的角度来看,当我得到一组 NSManagedObjects 时,它们不在 RAM 中。但在排序过程中,它们会出现在 RAM 中。

我想对 NSMutableSet 进行排序(尝试设置:

NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"specificImages.id" ascending:YES];

但我得到了

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'

我描述的这个问题是另一个问题NSSortDescriptor to-many relationships

从我的角度来看,我看到了 2 种方法: 1)执行FetchRequest,但提供:对象不会出现在RAM中(我不知道如何,但我几乎确定这是可能的) 2)更改我的快速排序以提供:对象不会出现在 RAM 中

更新 这是我的代码,我尝试使用 executeFetchRequest 获取对象(对象在 RAM 中,我希望它是错误对象(不在 RAM 中)):

+ (NSMutableSet *)GetImagesWithPredicate:(NSPredicate *)predicate

    NSString *entityName = kEntityName;
    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];;
    NSManagedObjectContext *context = appDelegate.managedObjectContext;
    NSEntityDescription *entityDesctiption = [NSEntityDescription 
                                              entityForName: entityName
                                              inManagedObjectContext:context];

    // find object
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDesctiption];
    [request setPredicate:predicate];
    NSArray *objects = [context executeFetchRequest:request error:nil];
    [request release];
    if (objects == nil)
    
        NSLog(@"there was an error");
        return nil;
    
    NSMutableSet *set = [NSMutableSet setWithArray:objects];
    return set;

谓词:

NSMutableArray *allID = [NSMutableArray array];
for (int i = 1; i < 639; i++)

      [allID addObject:[NSNumber numberWithInt:i]];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id in %@", allID];

【问题讨论】:

仅供参考,您不应该在单个实体/行中存储 5MB 的数据。 Apple 指南明确讨论了这一点。除非图像非常小,否则应将图像存储在文件系统中,并且仅将其路径存储在实体中。 您能否明确证明指向 Apple 指南的链接。所以我可以阅读它。 (我听说过,但我想自己读) 【参考方案1】:

tl;dr 代码。

CoreData 有一个东西。这称为故障。故障对象意味着您引用了该对象的数据仍在存储中(您的“不在 RAM 中”的版本)。要默认一个对象,您必须调用valueForKey: 方法并获取任何属性(这会加载除关系之外的整个对象)。在进行排序时,核心数据会在内部调用valueForKey:,这会导致您的图像数据被加载。

我对您的建议:创建一个名为 ImageDescriptor 的单独实体,您可以在其中保存排序所需的所有属性。该实体将与实际的二进制图像数据保持一对一的关系,您仅在需要时才获取(即使用延迟获取)。

【讨论】:

好主意,但我希望,可能还有其他方法:1)在我使用 valueForKey 之后再次隐藏所有数据:2)使用 Fault 对象(不在 RAM 中)执行 FetchRequest ) 1) 您不能在排序过程中对对象进行故障处理(这需要为您从中获取对象的上下文中的每个对象调用 refreshObject:mergeChanges: 方法调用。2)AFAIK - @987654325 @ 默认检索故障对象。 如果executeFetchRequest:默认检索故障对象,为什么应用程序会收到内存警告。我将为您更新问题。我将使用 executeFetchRequest 添加代码: 您可以尝试简化谓词。包含 600 个对象的数组可以以不同的方式呈现 (id &lt; 689)。您还可以尝试使用排序描述符设置获取限制。这样,您将获得排序的结果,但不是整个批次,而是其中的一部分。 1) id 可以不同,id 【参考方案2】:

我使用了 Eimantas 方法,但我知道了如何获取对象而不将它们记录在 RAM 中。 我用includesPropertyValues

【讨论】:

以上是关于核心数据。对大尺寸 NSManagedObject 的 NSMutableSet 进行排序的主要内容,如果未能解决你的问题,请参考以下文章

是不是不能复制一个核心数据NSManagedObject?

核心数据 NSManagedObject 更改

核心数据 - NSManagedObject(有关系)到 JSON

核心数据 NSManagedObject:最大属性数?

将 NSManagedObject 更新为核心数据

核心数据 - 遍历 NSManagedObject 的属性