2016-06-15 28 views
6

Tôi đang đối mặt với một cơn ác mộng của một vụ tai nạn trong performBatchUpdates trên chế độ xem bộ sưu tập.Cơn ác mộng với tai nạn performBatchUpdates

Vấn đề về cơ bản là: Tôi có rất nhiều hình ảnh trên một thư mục trên máy chủ. Tôi muốn hiển thị hình thu nhỏ của những tệp đó trên chế độ xem bộ sưu tập. Nhưng hình thu nhỏ phải được tải xuống từ máy chủ không đồng bộ. Khi họ đến nơi, họ sẽ được đưa vào chế độ xem bộ sưu tập bằng cách sử dụng một cái gì đó như thế này:

dispatch_async(dispatch_get_main_queue(), 
      ^{ 
       [self.collectionView performBatchUpdates:^{ 

       if (removedIndexes && [removedIndexes count] > 0) { 
        [self.collectionView deleteItemsAtIndexPaths:removedIndexes]; 
       } 

       if (changedIndexes && [changedIndexes count] > 0) { 
        [self.collectionView reloadItemsAtIndexPaths:changedIndexes]; 
       } 

       if (insertedIndexes && [insertedIndexes count] > 0) { 
        [self.collectionView insertItemsAtIndexPaths:insertedIndexes]; 
       } 

       } completion:nil]; 
      }); 

vấn đề là điều này (tôi nghĩ). Giả sử tại thời điểm = 0, chế độ xem bộ sưu tập có 10 mục. Sau đó tôi thêm 100 tệp nữa vào máy chủ. Ứng dụng sẽ thấy các tệp mới và bắt đầu tải xuống các hình thu nhỏ. Khi hình thu nhỏ tải xuống, chúng sẽ được chèn vào chế độ xem bộ sưu tập. Nhưng vì tải xuống có thể mất nhiều thời gian khác nhau và thao tác tải xuống này không đồng bộ, tại một thời điểm iOS sẽ mất theo dõi số lượng bộ sưu tập có và toàn bộ nội dung sẽ bị lỗi với thông báo khét tiếng thảm khốc này.

*** Chấm dứt ứng dụng do ngoại lệ còn tự do 'NSInternalInconsistencyException', lý do: 'cập nhật không hợp lệ: không hợp lệ số mục trong phần 0. Số lượng các mục chứa trong một phần hiện sau khi cập nhật (213) phải bằng số lượng mục có trong phần đó trước bản cập nhật (154), cộng hoặc trừ số mục được chèn hoặc xóa khỏi phần đó (40 được chèn, 0 đã xóa) và cộng hoặc trừ số lượng mục được chuyển vào hoặc ra khỏi phần đó (0 đã chuyển vào, 0 đã chuyển đi). '

Bằng chứng tôi có điều gì đó đang xảy ra là nếu tôi in tổng số mục trên tập dữ liệu tôi thấy chính xác 213. Vì vậy, tập dữ liệu khớp với số chính xác và thông báo là vô nghĩa.

Tôi đã gặp sự cố này trước đây, here nhưng đó là dự án iOS 7. Bằng cách nào đó, sự cố đã được trả về ngay trên iOS 8 và các giải pháp không hoạt động và giờ là tập dữ liệu IS IN SYNC.

Trả lời

1

Tôi nghĩ rằng vấn đề là do các chỉ mục gây ra.

chính:

  • Đối với các mặt hàng được cập nhật và xóa, các chỉ số phải là các chỉ số của mục gốc.
  • Đối với các mục được chèn, chỉ mục phải là chỉ mục của các mục cuối cùng.

Đây là một code demo với ý kiến:

class CollectionViewController: UICollectionViewController { 

    var items: [String]! 

    let before = ["To Be Deleted 1", "To Be Updated 1", "To Be Updated 2", "To Be Deleted 2", "Stay"] 
    let after = ["Updated 1", "Updated 2", "Added 1", "Stay", "Added 2"] 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Refresh", style: .Plain, target: self, action: #selector(CollectionViewController.onRefresh(_:))) 

     items = before 
    } 

    func onRefresh(_: AnyObject) { 

     items = after 

     collectionView?.performBatchUpdates({ 
      self.collectionView?.deleteItemsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0), NSIndexPath(forRow: 3, inSection: 0), ]) 

      // Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete and reload the same index path 
      // self.collectionView?.reloadItemsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0), NSIndexPath(forRow: 1, inSection: 0), ]) 

      // NOTE: Have to be the indexes of original list 
      self.collectionView?.reloadItemsAtIndexPaths([NSIndexPath(forRow: 1, inSection: 0), NSIndexPath(forRow: 2, inSection: 0), ]) 

      // Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert item 4 into section 0, but there are only 4 items in section 0 after the update' 
      // self.collectionView?.insertItemsAtIndexPaths([NSIndexPath(forRow: 4, inSection: 0), NSIndexPath(forRow: 5, inSection: 0), ]) 

      // NOTE: Have to be index of final list 
      self.collectionView?.insertItemsAtIndexPaths([NSIndexPath(forRow: 2, inSection: 0), NSIndexPath(forRow: 4, inSection: 0), ]) 

     }, completion: nil) 
    } 

    override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { 
     return 1 
    } 
    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
     return items.count 
    } 

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { 
     let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) 

     let label = cell.viewWithTag(100) as! UILabel 

     label.text = items[indexPath.row] 

     return cell 
    } 
} 
3

Có vẻ như bạn cần thực hiện thêm một chút công việc với việc tạo nhóm hình ảnh đã xuất hiện cho mỗi nhóm 'hoạt hình'. Từ đối phó với tai nạn như thế này trước đây, cách thức hoạt động của 'performBatchUpdates' là

  1. Trước khi gọi khối của bạn, nó sẽ kiểm tra đôi tất cả các số lượng mục và lưu chúng bằng cách gọi 'numberOfItemsInSection' (đây là 154 trong thông báo lỗi của bạn).
  2. Nó chạy khối, theo dõi chèn/xóa và tính số lượng cuối cùng của các mục nên được dựa trên chèn và xóa.
  3. Sau khi khối được chạy, nó sẽ kiểm tra số đếm được tính theo số lượng thực tế khi nó yêu cầu dữ liệu của bạnSource 'numberOfItemsInSection' (đây là số 213). Nếu nó không phù hợp, nó sẽ sụp đổ.

Dựa trên các biến 'insertIndexes' và 'changedIndexes', bạn tính toán trước những thứ cần hiển thị dựa trên phản hồi tải xuống từ máy chủ và sau đó chạy hàng loạt. Tuy nhiên, tôi đoán phương pháp 'numberOfItemsInSection' của bạn luôn trả về số lượng 'đúng' của các mục.

Vì vậy, nếu quá trình tải xuống hoàn tất trong bước 2, khi quá trình này thực hiện kiểm tra độ chính xác trong '3', các số của bạn sẽ không xếp hàng nữa.

Giải pháp dễ nhất: Chờ cho đến khi tất cả các tệp đã được tải xuống, sau đó thực hiện một 'batchUpdates' duy nhất. Có lẽ không phải là trải nghiệm người dùng tốt nhất nhưng nó tránh được vấn đề này.

Giải pháp khó hơn: Thực hiện các lô nếu cần và theo dõi các mục nào đã hiển thị/hiện đang hoạt ảnh riêng biệt với tổng số mục. Một cái gì đó như:

BOOL _performingAnimation; 
NSInteger _finalItemCount; 

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 
    return _finalItemCount; 
} 

- (void)somethingDidFinishDownloading { 
    if (_performingAnimation) { 
     return; 
    } 
    // Calculate changes. 
    dispatch_async(dispatch_get_main_queue(), 
      ^{ 
       _performingAnimation = YES; 
       [self.collectionView performBatchUpdates:^{ 

       if (removedIndexes && [removedIndexes count] > 0) { 
        [self.collectionView deleteItemsAtIndexPaths:removedIndexes]; 
       } 

       if (changedIndexes && [changedIndexes count] > 0) { 
        [self.collectionView reloadItemsAtIndexPaths:changedIndexes]; 
       } 

       if (insertedIndexes && [insertedIndexes count] > 0) { 
        [self.collectionView insertItemsAtIndexPaths:insertedIndexes]; 
       } 

       _finalItemCount += (insertedIndexes.count - removedIndexes.count); 
       } completion:^{ 
       _performingAnimation = NO; 
       }]; 
      }); 
} 

Điều duy nhất để giải quyết sau đó sẽ là để đảm bảo bạn chạy một kiểm tra cuối cùng cho các hạng mục còn sót lại nếu mục cuối cùng để tải xong trong một hình ảnh động (có thể có một phương pháp 'performFinalAnimationIfNeeded' mà bạn chạy trong khối hoàn thành)

+0

Tôi đang phải đối mặt với cùng một vấn đề. Tôi sẽ thử giải pháp của bạn. – vmeyer

Các vấn đề liên quan