尝试将服务器内容同步到 Core Data 时出现问题 - Magical Record 插入过程非常慢

Posted

技术标签:

【中文标题】尝试将服务器内容同步到 Core Data 时出现问题 - Magical Record 插入过程非常慢【英文标题】:Trouble when trying to sync server content into Core Data - Magical Record insertion process is extremely slow 【发布时间】:2014-07-31 07:46:38 【问题描述】:

我能够成功地将我的数据库同步到核心数据中。我使用MagicalRecord 作为我的核心数据包装接口。

我面临的问题是插入所有信息的过程真的很慢。 我知道这个问题可能太具体了。但我真的相信我遗漏了 Obj-C 代码中的一些微不足道的东西。

这是我尝试同步的部分数据库的结构

首先是一些背景信息。

每个地方都有:

one(1) 在 place_contact_info 中引用的行 one(1) 在 place_adjustments 中引用的行 七(7)在 place_opening_hours 中引用了行 零(0) 个或多个 在place_tag place_category 中引用了行。

数据库(服务器端)目前包含约 1000 个位置。

我在服务器端的 php 脚本以 JSON 格式提供此数据的表示形式。

它的交付方式类似于 this:

这是我对核心数据逻辑的插入。

data 这是上面链接中描述的部分数据库的完整表示。

+ (void)updateDatabaseWithPlaces:(NSDictionary *)data
   
    [FFSynchronizationHandler synchronizePlaces:[data objectForKey:@"places"]];
    [FFSynchronizationHandler synchronizeContactInfo:[data objectForKey:@"place_contact_info"]];
    [FFSynchronizationHandler synchronizeOpeningHours:[data objectForKey:@"opening_hours"]];
    [FFSynchronizationHandler synchronizeAdjustments:[data objectForKey:@"place_adjustments"]];
    [FFSynchronizationHandler synchronizeCategories:[data objectForKey:@"place_category"]];
    [FFSynchronizationHandler synchronizeTags:[data objectForKey:@"place_tag"]];

    [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfWithCompletion:^(BOOL success, NSError *error) 

        [[NSNotificationCenter defaultCenter]postNotificationName:SYNCHRONIZATION_COMPLETE object:nil];

    ];



+ (void)synchronizePlaces:(NSDictionary*)places

    for(id placeParams in places)
    
        Place *place = [Place MR_findFirstByAttribute:@"place_id" withValue:@([[placeParams objectForKey:@"id"]integerValue])];

        // if the place is flagged as deleted, delete that entity
        if([[placeParams objectForKey:@"is_deleted"]integerValue] == 1)
            [place MR_deleteEntity];
            continue;
         else if(!place)
            // if the place does not exists in the database already, create it.
            place = [Place MR_createEntity];
        
        place.place_id = @([[placeParams objectForKey:@"id"]integerValue]);
        place.name = [placeParams objectForKey:@"name"];
        place.longitude = @([[placeParams objectForKey:@"longitude"]floatValue]);
        place.latitude = @([[placeParams objectForKey:@"latitude"]floatValue]);
        place.main_category = @([[placeParams objectForKey:@"main_category"]integerValue]);
        place.info_text = [placeParams objectForKey:@"info_text"] == [NSNull null] ? nil :[placeParams objectForKey:@"info_text"];
    
    [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfAndWait];



+ (void)synchronizeContactInfo:(NSDictionary*)contactInfoParams

    for(id contactInfo in contactInfoParams)
    
        Place *place = [Place MR_findFirstByAttribute:@"place_id" withValue:@([[contactInfo objectForKey:@"place_id"] integerValue])];

        // if the place that the contact info belongs to
        // is somehow not here, just continue the loop
        if(place == nil)
            continue;
        PlaceContactInfos *place_contact_info = [PlaceContactInfos MR_createEntity];
        place_contact_info.website = [contactInfo objectForKey:@"website"] == [NSNull null] ? nil :[contactInfo objectForKey:@"website"];
        place_contact_info.telephone = [contactInfo objectForKey:@"telephone"] == [NSNull null] ? nil : [contactInfo objectForKey:@"telephone"];
        place_contact_info.email = [contactInfo objectForKey:@"email"] == [NSNull null] ? nil : [contactInfo objectForKey:@"email"];
        place_contact_info.address = [contactInfo objectForKey:@"address"] == [NSNull null] ? nil : [contactInfo objectForKey:@"address"];
        place_contact_info.city = [contactInfo objectForKey:@"city"] == [NSNull null] ? nil : [contactInfo objectForKey:@"city"];
        place_contact_info.zip_code = [contactInfo objectForKey:@"zip_code"] == [NSNull null] ? nil : [contactInfo objectForKey:@"zip_code"];
        place_contact_info.country = [contactInfo objectForKey:@"country"] == [NSNull null] ? nil : [contactInfo objectForKey:@"country"];
        place.place_contact_info = place_contact_info;

    
    [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfAndWait];


+ (void)synchronizeOpeningHours:(NSDictionary*)openingHoursParams

    for(id openingHours in openingHoursParams)
    
        NSNumber* openingHoursId = @([[openingHours objectForKey:@"id"]integerValue]);
        NSNumber* placeId = @([[openingHours objectForKey:@"place_id"]integerValue]);
        NSNumber* isDeleted = @([[openingHours objectForKey:@"is_deleted"] integerValue]);
        Place *place = [Place MR_findFirstByAttribute:@"place_id" withValue:placeId];

        // if the place is flagged as deleted, delete that entity
        if(isDeleted.integerValue > 0)
            PlaceOpeningHours *oh = [PlaceOpeningHours MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:@"place_id = %@ AND oh_id = %@",placeId, openingHoursId]];
            if(oh)
                [oh MR_deleteEntity];
         else 
            PlaceOpeningHours *oh = [PlaceOpeningHours MR_createEntity];
            oh.day =  @([[openingHours objectForKey:@"day"]integerValue]);
            oh.open = [openingHours objectForKey:@"open"] == [NSNull null] ? nil : [openingHours objectForKey:@"open"];
            oh.close = [openingHours objectForKey:@"close"] == [NSNull null] ? nil : [openingHours objectForKey:@"close"];
            oh.always_open = @([[openingHours objectForKey:@"always_open"]integerValue]);
            oh.is_closed = @([[openingHours objectForKey:@"is_closed"]integerValue]);
            oh.place_id = placeId;
            oh.place = place;
        

    
     [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfAndWait];



+ (void)synchronizeAdjustments:(NSDictionary*)adjustmentsParams

    for(id adjustmentParams in adjustmentsParams)
    
        Place *place = [Place MR_findFirstByAttribute:@"place_id" withValue:@([[adjustmentParams objectForKey:@"place_id"] integerValue])];
        // if the place that the adjustments belongs to
        // is somehow not here, just continue the loop
        if(place == nil)
            continue;

        PlaceAdjustments *place_adjustments = [PlaceAdjustments MR_createEntity];
        place_adjustments.parking = @([[adjustmentParams objectForKey:@"parking"]integerValue]);
        place_adjustments.elevator = @([[adjustmentParams objectForKey:@"elevator"]integerValue]);
        place_adjustments.toilet = @([[adjustmentParams objectForKey:@"toilet"]integerValue]);
        place_adjustments.ramp = @([[adjustmentParams objectForKey:@"ramp"]integerValue]);
        place_adjustments.availability = @([[adjustmentParams objectForKey:@"availability"]integerValue]);
        [place setPlace_adjustments:place_adjustments];
    
    [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfAndWait];


+ (void)synchronizeCategories:(NSDictionary*)placeCategoryParams

    for(id categoryParams in placeCategoryParams)
    
        NSNumber* categoryId = @([[categoryParams objectForKey:@"category_id"] integerValue]);
        NSNumber* placeId = @([[categoryParams objectForKey:@"place_id"]integerValue]);
        NSNumber* isDeleted = @([[categoryParams objectForKey:@"is_deleted"] integerValue]) ;
        Place *place = [Place MR_findFirstByAttribute:@"place_id" withValue:placeId];
        if(isDeleted.integerValue > 0)
            PlaceCategories *cat = [PlaceCategories MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:@"place_id = %@ AND category_id = %@",placeId, categoryId]];
            if(cat)
                [cat MR_deleteEntity];
             else 
            PlaceCategories *cat = [PlaceCategories MR_createEntity];
            cat.category_id = categoryId;
            cat.place_id = placeId;
            cat.place = place;
        

    
    [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfAndWait];


+ (void)synchronizeTags:(NSDictionary*)placeTagParams

    for(id placeTag in placeTagParams)
    
        NSNumber* tagId = @([[placeTag objectForKey:@"id"]integerValue]);
        NSNumber* placeId = @([[placeTag objectForKey:@"place_id"] integerValue]);
        NSNumber* isDeleted = @([[placeTag objectForKey:@"is_deleted"] integerValue]);

        Place *place = [Place MR_findFirstByAttribute:@"place_id" withValue:placeId];
        if(isDeleted.integerValue == 1)
            PlaceTags *tag = [PlaceTags MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:@"place_id = %@ AND tag_id = %@",placeId, tagId]];
            if(tag)
                [tag MR_deleteEntity];

         else 
            PlaceTags *tag = [PlaceTags MR_createEntity];
            tag.tag = [placeTag objectForKey:@"tag"];
            tag.place_id = placeId;
            tag.place = place;
        

    
    [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfAndWait];

从第一行代码到

 [[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfWithCompletion:^(BOOL success, NSError *error) 

            [[NSNotificationCenter defaultCenter]postNotificationName:SYNCHRONIZATION_COMPLETE object:nil];

        ];

被称为大约一分钟通行证。

如何优化?将其插入 mysql 只需一秒钟。 感谢您的关注。

编辑 1

经过一些分析后,我意识到开放时间和标签的同步需要花费大量时间。

每次运行的MR_findFirstMatchingAttribute 是时间窃贼。您对如何优化有什么建议吗?

【问题讨论】:

你能发布核心数据获取和保存的仪器跟踪吗?时间档案也会很有帮助。 也许第一步是查看 Core Data 的文档并为大部分数据执行批量插入 developer.apple.com/library/ios/documentation/Cocoa/Conceptual/… @quellish 查看我的更新。谢谢 【参考方案1】:

看起来您正在主/默认上下文中执行所有操作。这本质上是主线程上下文。通过调用 saveOnlySelfAndWait,您正在阻塞主线程。这可能会导致一些同步锁。我建议创建一个全新的后台上下文,并在那里执行所有保存。

【讨论】:

以上是关于尝试将服务器内容同步到 Core Data 时出现问题 - Magical Record 插入过程非常慢的主要内容,如果未能解决你的问题,请参考以下文章

将 Core Data 添加到现有 Xcode 项目时出现未声明的标识符错误

将 UIImage 保存到 Core Data 时出错

Core-Data:想要将新的 web xml 内容持久保存到我的数据存储中,而不是替换现有的

尝试将 JSON 文件展平为 CSV 时出现错误消息

尝试将RoleManager注入ASP.NET Core 2.2控制器时出现错误

Onenote2013在同步时出现:抱歉,同步时出错.将稍后再试. (错误代码: 0xE4020017)