2016-11-16 52 views
1

我在Swift中遇到Realm result set問題。當Realm結果更新時,我使用Realm通知塊來更新我的TableView。TableView在更新領域結果時拋出NSInternalInconsistencyException

但是,在更新TableView期間,有時會將新項目添加到Realm結果中,導致NSInternalInconsistencyException

我的環境:

場景:

所以我們可以說我正在發送一條聊天消息,同時也收到一條聊天消息。

  1. 我正在添加發送給Realm數據庫的消息。
  2. 我的領域通知塊將開始更新TableView。
  3. 同時,我收到來自某人的聊天消息,並將其添加到Realm數據庫中。
  4. TableView更新已完成並引發NSInternalInconsistencyException,因爲更新開始時Realm的大小與更新結束時的大小不同(因爲收到的消息已添加到Realm數據庫中)。

異常時引發:

2016-11-16 21:03:57.727510 MyApp[9042:2331360] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (98) must be equal to the number of rows contained in that section before the update (97), plus or minus the number of rows inserted or deleted from that section (10 inserted, 10 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).' 
*** First throw call stack: 
(0x18f5021c0 0x18df3c55c 0x18f502094 0x18ff8f79c 0x19554001c 0x1000faeb8 0x10137619c 0x10134d4d0 0x100bf4b88 0x100bf4754 0x100b427fc 0x100b425f0 0x100ba0004 0x100d3072c 0x100d878b8 0x100d8840c 0x100d883e4 0x18f4b0278 0x18f4afbc0 0x18f4ad7c0 0x18f3dc048 0x190e62198 0x1953c82fc 0x1953c3034 0x100105be8 0x18e3c05b8) 
libc++abi.dylib: terminating with uncaught exception of type NSException 

重現步驟:

  1. 創建的TableView控制器。
  2. 使用realm.objects(ChatMessage.self)獲取一些結果。使用Realm collection notification更新TableView。
  3. 使用0.1秒的時間間隔向Realm添加新消息。
  4. 構建並運行應用程序。
  5. 等待幾秒鐘和應用程序崩潰與NSInternalInconsistencyException

我的視圖控制器:

這是導致崩潰的代碼。

import UIKit 
import RealmSwift 
import Alamofire 
import AlamofireObjectMapper 

class ChatTestViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 

    @IBOutlet weak var tableView: UITableView! 

    let realm = try! Realm() 

    let textCellMeIdentifier = "ChatMessageCellMe" 

    var results: Results<ChatMessage>? 

    var ticket: Ticket? 

    var notificationToken: NotificationToken? 

    var tableViewTempCount = 0 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     ticket = realm.objects(Ticket.self).first 
     results = realm.objects(ChatMessage.self) 

     initializeTableView() 

     notificationToken = results?.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in 
      guard let tableView = self?.tableView else { return } 

      switch changes { 
      case .initial: 
       tableView.reloadData() 
       break 
      case .update(_, let deletions, let insertions, let modifications): 
       tableView.beginUpdates() 
       tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }), with: .none) 
       tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}), with: .none) 
       tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }), with: .none) 
       tableView.endUpdates() 
       break 
      case .error(let error): 
       fatalError("\(error)") 
       break 
      } 
     } 

     _ = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.sendRandomMessage), userInfo: nil, repeats: true) 
    } 

    deinit { 
     notificationToken?.stop() 
    } 

    private func initializeTableView() { 
     tableView.register(UINib(nibName: textCellMeIdentifier, bundle: Bundle.main), forCellReuseIdentifier: textCellMeIdentifier) 

     tableView.delegate = self 
     tableView.dataSource = self 
    } 

    @objc func sendRandomMessage() { 
     tableViewTempCount += 1 

     let parameters: Parameters = [ 
      "body": "Random Test: " + String(describing: tableViewTempCount), 
     ] 

     let tempMessage = ChatMessage() 
     tempMessage.id = "-" + String(describing: tableViewTempCount) 
     tempMessage.ticketId = ticket!.id 
     tempMessage.sent = false 
     tempMessage.body = parameters["body"]! as! String 
     tempMessage.by = ChatMessage.By.me.rawValue 
     tempMessage.createdAt = Date() 

     let realm = try! Realm() 

     try! realm.write { 
      /// Add a temporary message to the TableView (and remove it if the server returned the saved message) 
      realm.add(tempMessage, update: true) 

//   _ = ChatMessageClient.create(ticket!, parameters) { (error: Error?, chatMessages: [ChatMessage]?) in 
//    /// The server responded with the message and it was inserted in our Realm database, so delete the temp message 
//    realm.delete(tempMessage) 
//   } 
     } 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return results!.count 
    } 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let row = results?[indexPath.row] 

     let cell:ChatMessageCell = (tableView.dequeueReusableCell(withIdentifier: textCellMeIdentifier, for: indexPath as IndexPath) as? ChatMessageCell)! 

     cell.body.text = row?.body 
     cell.container.alpha = (row?.sent)! ? 1.0 : 0.4 

     return cell 
    } 

} 

我希望有人可以幫助我防止這種異常被拋出。

+0

如果您不使用0.1計時器,而只是簡單地調用sendRandomMessage()函數,您是否也會崩潰? – Mikael

+1

@Mikael是的,它也沒有計時器崩潰。我認爲jpsim已經給出了正確的答案。但我仍在驗證。 – Tijme

回答

2

我們最近修復了Realm收集通知機制中的一些競爭條件,這可能會導致您在此處看到的異常。請參閱https://realm.io/news/realm-objc-swift-2.1/

您是否可以使用Realm Swift 2.1.0再試一次,如果能解決您的問題,請告訴我們。謝謝!

+0

它就像一個魅力。感謝更新。 – Tijme