2016-02-16 61 views
2

喜歡StackOverflow的朋友。TableView無效更新

我在我的應用程序上有一個聊天屏幕,我根據數組的實際大小執行插入和刪除操作。看看這個:

func addObject(object: Object?) { 

    if comments == nil || object == nil || object?.something == nil || object?.anything == nil { 
     return 
    } 

    self.objectsTableView.beginUpdates() 

    if self.objects!.count == 10 { 
     self.objects?.removeAtIndex(9) 
     self.objectsTableView.deleteRowsAtIndexPaths([NSIndexPath(forRow : 9, inSection: 0)], withRowAnimation: .Right) 
    } 

    self.objects?.insert(object!, atIndex: 0) 
    self.objectsTableView.insertRowsAtIndexPaths([NSIndexPath(forRow : 0, inSection: 0)], withRowAnimation: .Right) 

    self.objectsTableView.endUpdates() 

    } 

但經過一些壓力測試,日誌通知:

無效更新:在第0行的數目無效的包含在現有的部分更新後 行數(1)必須爲 ,其等於 更新(10)之前該部分中包含的行數,加上或減去從 插入或刪除的部分(插入1個,刪除0)以及加或減的行數移入或移出該部分的行數爲 (0移入,0搬出)。

我不知道發生了什麼,只有當對象的插入非常極端時纔會發生這種情況,例如每0.2秒一次。

有人知道我可以做嗎?

+2

是否要更新在多個線程中的'self.objects'數組:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let count = self.objects?.count ?? 0 if self.semaphore != nil && semaphoreCode == BLOCKED_STATE { dispatch_semaphore_signal(self.semaphore!) } return count } 

該添加對象的方法? Swift數組不是線程安全的,所以你應該採取適當的預防措施,以precent併發更新 – Paulw11

+0

我使用這個:https://gist.github.com/ppamorim/845517fed6bec0fd41ff –

+0

所以是的,你是。你需要保護你的數組更新 - 請參閱馬特橋樑在這裏回答 - http://stackoverflow.com/questions/24045895/what-is-the-swift-equivalent-to-objective-cs-synchronized(不是接受的答案) – Paulw11

回答

0

是解決這個問題的人的名字:信號燈

錯誤仍然發生,但僅限於列表中的大量項目。我不知道會是什麼。

的數據源協議:

func addObject(object: Object?) { 

    if object == nil { 
     return 
    } 

    if self.semaphore != nil { 
     let tempSemaCode = dispatch_semaphore_wait(semaphore!, 100000) 
     if tempSemaCode == BLOCKED_STATE { 
     self.semaphoreCode = RELEASED_STATE 
     } 
    } 

    if self.objects != nil && semaphoreCode != BLOCKED_STATE { 

     var needsRemoveLastItem = false 
     if self.objects!.count == 10 { 
     self.objects?.removeAtIndex(9) 
     needsRemoveLastItem = true 
     } 
     self.objects?.insert(object!, atIndex: 0) 

     if self.objects!.count > 0 { 
     self.objectsTableView.beginUpdates() 
     if needsRemoveLastItem { 
      self.objectsTableView.deleteRowsAtIndexPaths([NSIndexPath(forRow : 9, inSection: 0)], withRowAnimation: .Right) 
     } 
     self.objectsTableView.insertRowsAtIndexPaths([NSIndexPath(forRow : 0, inSection: 0)], withRowAnimation: .Right) 
     self.objectsTableView.endUpdates() 
     self.semaphore = dispatch_semaphore_create(BLOCKED_STATE) 
     } 

    } 

    } 
1

模型失配

包含在現有段的行的更新後的數字(1)必須更新(10)之前等於包含在該部分的行數,加或減號插入或從部分已刪除的行數(插入1,0刪除)

在純英文的理性的人,在UITableView認爲你應該有11行:
10在更新之前+ 1插入。

數更新後包含在現有段的行(1)

...指numberOfRowsInSection正在返回1爲第0,這表明objects陣列是不同步的,假設你使用類似如下:

override func tableView(tableView: UITableView, 
         numberOfRowsInSection section: Int) -> Int { 
    return objects.count 
} 

使用NSFetchedResultsController

一個乾淨的解決方案是使用NSFetchedResultsController是你的模型和UI之間的接口。它很好地研究了樣板代碼,是確保線程安全的一個很好的平臺。 Documentation here


注:

整潔的效果!細胞似乎旋轉到頂部。
我無法使用您製作的Gist來分解它,也無法安排多個併發測試。您的Object陣列必須有盜賊訪問

演示

這簡化版本的作品。只是勾doPlusAction一個按鈕動作,看它的循環:

neat effect

class TableViewController: UITableViewController { 
    var objects:[Int] = [0,1,2,3,4] 
    var insertions = 5 

    @IBAction func doPlusAction(sender: AnyObject) { 
     tableView.beginUpdates() 

     objects.removeAtIndex(4) 
     tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: 4, inSection: 0)], withRowAnimation: .Right) 
     objects.insert(insertions++, atIndex: 0) 
     tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)], withRowAnimation: .Right) 
     tableView.endUpdates() 

     let delay = 0.1 * Double(NSEC_PER_SEC) //happens the same with this too, when reach 100-150 items 
     let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) 
     dispatch_after(time, dispatch_get_main_queue()) {() -> Void in 
      self.doPlusAction(self) 
     } 
    } 

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return objects.count 
    } 

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) 
     cell.textLabel!.text = "Cell \(objects[indexPath.row])" 
     return cell 
    } 
} 

+0

仍崩潰:( –

+0

我試過用'NSBlockOperation'添加一個'dispatch_semaphore_create'。不工作。 –

+0

看看我的回答。 –