2014-11-15 60 views
0

我有一個loadItem函數,它是假設從一個分析服務器加載項目。這包括一個循環,我在結束時將數據保存到一個數組:reloadData當循環完成

itemArray?.addObject(arrayDic) 

當這個被保存我想給的CollectionView的reloadData。因此我已經將它插入到dispatch_async塊中,但它似乎仍然是在將數據保存到itemArray數組之前運行的,因爲itemArray.count爲0,並且我在循環中檢查了數據保存到陣列。我究竟做錯了什麼?

func loadItems() { 
    var query = PFQuery(className:"Items") 
    query.orderByAscending("createdAt") 
    query.findObjectsInBackgroundWithBlock { 
     (objects: [AnyObject]!, error: NSError!) -> Void in 
     if error == nil { 

      for object in objects { 

       var imageRelation: PFRelation = object.relationForKey("pictures") 


       var query = imageRelation.query() 
       query.orderByDescending("createdAt") 

       query.findObjectsInBackgroundWithBlock { 
        (imageObjects: [AnyObject]!, error: NSError!) -> Void in 
        if error == nil { 

         var theArray = imageObjects as NSArray 

         var arrayDic = NSMutableDictionary() 


         let imageFile = theArray.objectAtIndex(0).objectForKey("image") as PFFile 
         imageFile.getDataInBackgroundWithBlock { 
          (imageData: NSData!, error: NSError!) -> Void in 
          if !(error != nil) { 
           let image = UIImage(data:imageData) 
           arrayDic.setValue(object.objectForKey("price"), forKey: "price") 
           arrayDic.setValue(image, forKey: "image") 
           itemArray?.addObject(arrayDic) 

          } 
         } 


        } 
       } 



      } 

      dispatch_async(dispatch_get_main_queue()) { 

       println(itemArray?.count) 
       self.collectionView.reloadData() 

      } 
     } else { 
      // Log details of the failure 
      NSLog("Error: %@ %@", error, error.userInfo!) 
     } 
    } 
} 
+0

它看起來像你正在做兩個嵌套調用「'query.findObjectsInBackgroundWithBlock'」,你的'reloadData'調用發生在'findObjectsInBackgroundWithBlock'外部。如果將'reloadData'放入內部塊,會發生什麼? –

+0

那麼它會循環幾次? –

+0

由於外部'findObjectsInBackgroundWithBlock'已經在後臺運行,爲什麼不重構內部塊以通過'findObjects'同步運行(或作爲外部循環的一部分)呢?然後,當一切都完成後,你可以在外部(也是唯一)塊的末尾調用'reloadData'。 –

回答

0

我不會建議試圖使請求同步(如能顯著減緩的過程中,除非你安排他們在一些併發的背景隊列同時運行)。相反,我會建議一種模式,允許您保持異步請求,但會通知您其完成。一個這樣的模式是調度組,在其中:

  • 創建調度組:

    let group = dispatch_group_create() 
    
  • 你循環的每次迭代中,調用dispatch_group_enter

    dispatch_group_enter(group) 
    
  • 贏得異步方法的完成塊,請致電dispatch_group_leave

    dispatch_group_leave(group) 
    
  • 指定你想要做什麼調度組完成時:

    dispatch_group_notify(group, dispatch_get_main_queue()) { 
        println(self.itemArray?.count) 
        self.collectionView.reloadData() 
    } 
    

    當所有的呼叫dispatch_group_enter都是由最終dispatch_group_leave通話偏移,通知塊將被調用爲您服務。

所以,看通過對象的代碼,你循環,它可能看起來像:

let group = dispatch_group_create() 

for object in objects { 

    dispatch_group_enter(group) 

    // do some stuff 

    query.findObjectsInBackgroundWithBlock { 
     (imageObjects: [PFObject]!, error: NSError!) -> Void in 

     if error == nil { 

      // do some more stuff 

      imageFile.getDataInBackgroundWithBlock { 
       (imageData: NSData!, error: NSError!) -> Void in 

       if !(error != nil) { 
        // do even some more stuff 
       } 

       dispatch_group_leave(group) 
      } 
     } else { 
      dispatch_group_leave(group) 
     } 
    } 
} 

dispatch_group_notify(group, dispatch_get_main_queue()) { 
    println(self.itemArray?.count) 
    self.collectionView.reloadData() 
} 

他們的其他功能等同的模式(如自定義的併發調度隊列調度的最後reloadData與屏障;使這些個別請求NSOperation對象,您添加到NSOperationQueue並創建依賴於其他操作的單獨的完成操作;等等),但調度組似乎需要對此​​代碼重構最少。無論如何,希望這可以說明基本思想:只有當其他異步請求完成時纔會觸發最終重新加載。

+0

也可以使用NSOperationQueue與塊操作 – nielsbot

+0

是的,這也適用。但是如果你正在處理異步任務,那麼你必須使'NSOperation'子類包裝這個異步任務(或者使它們以某種方式同步)。我經常更喜歡'NSOperation'子類方法(很好地封裝了網絡請求的細節),但我希望儘可能簡單。 OP似乎對GCD很熟悉,所以我想讓他保持在舒適區。 – Rob