Magic Writing Saving Large Amounts of Data to DB

I am using magic writing to store large amounts of data in my database. I want to keep it in the background, but it freezes the UI. I save it with

[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
    } completion:^(BOOL success, NSError *error) {
    }];

      

Therefore, it should not block the user interface. But I found that it uses the main thread for some reason to merge changes or something. I would be very grateful if anyone could suggest a solution on how to make this more efficient.

The code I'm using:

- (void)saveRidesForUser:(User *)user fromResponseData:(id)responseData
{

    if (![[[responseData valueForKey:@"rides"] class] isSubclassOfClass:[NSArray class]]) {
        return;
    }
    NSArray *rides = [responseData valueForKey:@"rides"];

    if (!rides)
        return;

    NSMutableArray *photoCacheArray = [[NSMutableArray alloc] init];

    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {

        User *rideUser = [user MR_inContext:localContext];

        for (NSDictionary *rideDictionary in rides) {

                Ride *ride = [Ride MR_createEntityInContext:localContext];
                ride.title = ([rideDictionary objectForKey:@"title"]) ? [rideDictionary valueForKey:@"title"] : @"";

                NSNumber *rideTimeStamp =[rideDictionary objectForKey:@"start_date"];
                if (rideTimeStamp)
                    ride.startDate = [NSDate dateWithTimeIntervalSince1970:[rideTimeStamp doubleValue]];

                NSNumber *rideEndTimeStamp =[rideDictionary objectForKey:@"end_date"];
                if (rideEndTimeStamp)
                    ride.endDate = [NSDate dateWithTimeIntervalSince1970:[rideEndTimeStamp doubleValue]];

                ride.serverID = [NSNumber numberWithInt:[[rideDictionary objectForKey:@"id"] intValue]];
                ride.isPopular = [NSNumber numberWithBool:NO];

                if ([rideDictionary objectForKey:@"intervals"]) {
                    NSArray *intervals = [rideDictionary objectForKey:@"intervals"];

                    for (NSDictionary *intervalDictionary in intervals) {

                        RideInterval *interval = [RideInterval MR_createEntityInContext:localContext];

                        interval.startDate = [NSDate dateWithTimeIntervalSince1970:[[intervalDictionary objectForKey:@"start_date"] integerValue]];
                        interval.endDate = [NSDate dateWithTimeIntervalSince1970:[[intervalDictionary objectForKey:@"end_date"] integerValue]];

                        interval.ride = ride;

                        if ([intervalDictionary objectForKey:@"points"] && ![[[intervalDictionary objectForKey:@"points"] class] isSubclassOfClass:[NSString class]]) {
                            NSArray *points = [intervalDictionary objectForKey:@"points"];

                            for (NSDictionary *pointDict in points) {

                                NSDictionary *pointDictionary = [pointDict dictionaryByReplacingNullsWithStrings];

                                RidePoint *point = [RidePoint MR_createEntityInContext:localContext];
                                point.timestamp = [NSDate dateWithTimeIntervalSince1970:[[pointDictionary objectForKey:@"timestamp"] integerValue]];

                                point.latitude = [NSNumber numberWithDouble:[[pointDictionary objectForKey:@"latitude"] doubleValue]];
                                point.longitude = [NSNumber numberWithDouble:[[pointDictionary objectForKey:@"longitude"] doubleValue]];
                                point.distanceFromPrevious = [NSNumber numberWithDouble:[[pointDictionary objectForKey:@"distance_from_previous"] doubleValue]];
                                point.interval = interval;
                            }
                        }
                    }
                }

                if ([rideDictionary valueForKey:@"photos"] && [[[rideDictionary valueForKey:@"photos"] class] isSubclassOfClass:[NSArray class]]) {
                    NSArray *photos = [rideDictionary objectForKey:@"photos"];

                    for (NSDictionary *photoDict in photos) {

                        NSDictionary *photoDictionary = [photoDict dictionaryByReplacingNullsWithStrings];
                        RidePhoto *photo = [RidePhoto MR_createEntityInContext:localContext];

                        photo.caption = [photoDictionary valueForKey:@"caption"];

                        NSNumber *timeStamp =[photoDictionary objectForKey:@"timestamp"];
                        if (timeStamp)
                            photo.timestamp = [NSDate dateWithTimeIntervalSince1970:[timeStamp doubleValue]];

                        photo.locationLatitude = [NSNumber numberWithDouble:[[photoDictionary objectForKey:@"location_latitude"] doubleValue]];
                        photo.locationLongitude = [NSNumber numberWithDouble:[[photoDictionary objectForKey:@"location_longitude"] doubleValue]];
                        photo.serverPath = [photoDictionary objectForKey:@"url"];
                        photo.imagePath = [NSString getUUID];
                        photo.ride = ride;

                        [photoCacheArray addObject:photo];
                    }
                }

                ride.user = rideUser;
            }

    } completion:^(BOOL success, NSError *error) {

        [[NSNotificationCenter defaultCenter] postNotificationName:kLTRideServise_RoutesDownloadedNotification object:nil userInfo:nil];

    }];

}

      

Thanks in advance for your help!

+3


source to share


3 answers


Are you using MR_defaultContext for all other main thread tasks?

I use saveWithBlock method: background save method, MR_defaultContext for main thread operations and it works great. As far as I know, MagicalRecord concatenates localContext with defaultContext at the end of the save operation.



Sample code:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.0 * NSEC_PER_SEC),      
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
        // your loop here in background thread
    } completion:^(BOOL success, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            // completion on main thread
        });
    }];
});

      

0


source


[[NSNotificationCenter defaultCenter] postNotificationName: kLTRideServise_RoutesDownloadedNotification object: nil userInfo: nil];

==> Check what processing you are doing on completion. This notification is being triggered on the main thread, you might be doing a lot of processing that keeps the main thread busy, causing the UI to freeze.



Also check if you are registered for NSManagedObjectContextDidSaveNotification from any context and see if you are doing any processing there.

0


source


I use

[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreWithCompletion: aCallback];

to save the context to the database.

This is described as

/// \ brief Asynchronously save changes in the current context all the way back to the persistent store
/// \ param completion Completion block that is called after the save has completed. The block is passed a success state as a `BOOL` and an` NSError` instance if an error occurs. Always called on the main queue.
/// \ discussion Executes asynchronous saves on the current context, and any ancestors, until the changes have been persisted to the assigned persistent store. The completion block will always be called on the main queue.

And it works great for me, without any freezing.

0


source







All Articles