简体   繁体   中英

Core Data. Sort NSMutableSet of big size NSManagedObject

I want to fetch about 200 NSManagedObject, size of each is about 5Mb. If I use executeFetchRequest, I get memory warning and app crashes (because fetched NSManagedObjects appears in the RAM of iPhone). If I can use executeFetchRequest, I can use sortdescriptor to sort data by id. But I can't (or may be I should set proper properties to NSFetchRequest, but I don't know, what properties). That's why I used another approach to load big amount of data: I have Entity Album, that has many entities SpecificImage. And I use this code to fetch all images of current album:

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

then I get array of NSManagedObjects

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

but then I try to get sorted array of this NSManagedObjects and I again get memory warning and app crashes. I tried to use

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

and

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

where

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

    }

}

but that didn't help.

From my perspective, when I get a set of NSManagedObjects, they are not in the RAM. But during sorting they appears in the RAM.

I wanted to get sorted NSMutableSet (tried to set :

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

But I got

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

I described this issue is the other question NSSortDescriptor to-many relationships

From my point of view, I see 2 approaches: 1) executeFetchRequest, but provide: the objects don't appear in the RAM ( I don't know how, but I'm almost shure that it's possible) 2) change my quick sort to provide: the objects don't appear in the RAM

Update Here is my code, where I try to fetch objects with executeFetchRequest (the objects are in RAM and I want it to be fault objects (not in the 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;
}

where predicate:

NSMutableArray *allID = [NSMutableArray array];
for (int i = 1; i < 639; i++)
{
      [allID addObject:[NSNumber numberWithInt:i]];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id in %@", allID];

tl;dr the code.

CoreData has a thing. It's called faulting . Fault object means that you have references to that object who's data is still in the store (your version of "not in RAM"). To de'fault an object you'd have to call valueForKey: method and fetch any property (this loads the whole object except for relationships). When doing sorting, core data internally calls valueForKey: which causes your image data to get loaded.

My proposal for you: create a separate entity called something like ImageDescriptor where you'd hold all the properties required for sorting. This entity would hold one-to-one relationship to actual binary image data which you would fetch only when needed (ie use lazy fetching).

I used Eimantas approach, but I got how to fetch objects withous recording them in RAM. I used includesPropertyValues

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM