2016-09-01 3 views
0

私はNSFetchedResultsControllerを使ってデータを取り込むテーブルを持っています。私はテーブルをリフレッシュするときに、50以上のアイテムをすべて更新する必要があるので、次のようにします。JSONデータを返すサーバーを呼び出し、 "media"オブジェクトを配列に格納し、この配列をループして個別に格納します各オブジェクトをコアデータ(バックグラウンドスレッド内)に変換し、テーブルをリロードします。これは正常に動作します。しかし、大きな問題があります。バックグラウンドで保存すると応答時間が遅くなる(iOS)

大規模な配列をループし、各オブジェクトをコアデータに個別に格納するため、データベースに保存するステップに7秒以上かかることがあります。このステップが実行されている間に、サーバーから他のデータを取得すると、応答時間が大幅に遅れます。保存プロセスが完了するまで、新しいデータを取得することはできません。これはバックグラウンドスレッドで実行され、他のサーバー呼び出しをブロックしていないはずなので、私はかなり混乱しています。

なぜデータをbgのコアデータに保存すると、応答時間が遅れるのですか?応答を中断することなく、大きなデータをコアデータに格納する方が良いでしょうか?

//Refreshing User Table method 
class func refreshUserProfileTable(callback: (error: NSError?) -> Void) { 
    //getProfile fetches data from server 
    ProfileWSFacade.getProfile(RequestManager.userID()!) { 
     (profile, isLastPage, error) ->() in 

     DataBaseManager.sharedInstance.saveInBackground({ (backgroundContext) in 
      let mediaList = profile?["media"] as? Array<JSONDictionary> 

      if let mediaList = mediaList { 
       //Response time is delayed when this loop is executing 
       for media in mediaList { 
        DataBaseManager.sharedInstance.storeObjectOfClass(Media.self, dict: media, context: backgroundContext) 
       } 
      } 
      }, completion: { 
       callback(error: error) 
     }) 
    } 
} 

//MARK: Core data methods: 
//Save in background method in Database manager 
func saveInBackground(
    block: (backgroundContext: NSManagedObjectContext) -> Void, 
    completion: (Void->Void)? = nil) 
{ 
    let mainThreadCompletion = { 
     if let completion = completion { 
      dispatch_async(dispatch_get_main_queue(), {() -> Void in 
       completion() 
      }) 
     } 
    } 

    backgroundContext.performBlock {() -> Void in 
     guard RequestManager.userID() != nil else { 
      mainThreadCompletion() 
      return 
     } 

     block(backgroundContext: self.backgroundContext) 

     if RequestManager.userID() != nil { 
      _ = try? self.backgroundContext.save() 
      DataBaseManager.sharedInstance.save() 
     } 

     mainThreadCompletion() 
    } 
} 

//Stores class object 
func storeObjectOfClass<T: NSManagedObject where T: Mappable>(
    entityClass:T.Type, 
    dict: JSONDictionary, 
    context: NSManagedObjectContext? = nil) -> T 
{ 
    let context = context ?? mainManagedObjectContext 
    let predicate = NSPredicate(format: "%K LIKE %@", entityClass.primaryKey(), entityClass.primaryKeyFromDict(dict)) 
    let requestedObject = DataBaseManager.createOrUpdateFirstEntity(
     entityType: T.self, 
     predicate: predicate, 
     context: context) { (entity) ->() in 
      entity.populateFromDictionary(dict) 
    } 

    return requestedObject 
} 

//Creates or updates core data entity 
class func createOrUpdateFirstEntity<T: NSManagedObject>(
    entityType entityType: T.Type, 
    predicate: NSPredicate, 
    context: NSManagedObjectContext, 
    entityUpdateBlock:(entity: T) ->()) -> T 
{ 
    guard DataBaseManager.sharedInstance.doPersistentStoreAvailible() else { return T() } 

    let desc = NSEntityDescription.entityForName(String(entityType), inManagedObjectContext: context)! 
    let existingEntityRequest = NSFetchRequest() 
    existingEntityRequest.entity = desc 
    existingEntityRequest.predicate = predicate 
    let requestedObject = try? context.executeFetchRequest(existingEntityRequest).first 

    if let requestedObject = requestedObject as? T { 
     entityUpdateBlock(entity: requestedObject) 
     return requestedObject 
    } else { 
     let newObject = T(entity: desc, insertIntoManagedObjectContext: context) 
     entityUpdateBlock(entity: newObject) 
     return newObject 
    } 
} 

答えて

2

私が最初に出て、最初の、.performBlockはFIFOのルールに従っていることが分かりました。ブロックが内部キューに入れられた順に実行されることを意味します:SO Link。そのため、次の残りの呼び出しは、保存する前に最初のブロックが完了するまで待機し、コールバックを行いました。実際の応答時間は遅くなかったが、FIFOのために保存時間に過ぎなかった。

解決策は、すべてのバックグラウンドコールで使用されていたものを使用するのではなく、プロファイル読み込みに別のNSManagedContextを使用することでした。

let profileContext: NSManagedObjectContext 

//Instead of calling saveInBackground, we save to saveInProfileContext, which wont block other rest calls. 
func saveInProfileContext(
    block: (profileContext: NSManagedObjectContext) -> Void, 
    completion: (Void->Void)? = nil) 
{ 

    let mainThreadCompletion = { 
     if let completion = completion { 
      dispatch_async(dispatch_get_main_queue(), {() -> Void in 
       completion() 
      }) 
     } 
    } 

    profileContext.performBlock {() -> Void in 
     guard RequestManager.userID() != nil else { 
      mainThreadCompletion() 
      return 
     } 

     block(profileContext: self.profileContext) 
     if RequestManager.userID() != nil { 
      _ = try? self.profileContext.save() 
      DataBaseManager.sharedInstance.save() 
     } 
     mainThreadCompletion() 
    } 
} 
関連する問題