2016-11-16 4 views
1

SwiftのRealm result setに問題があります。レルムの結果が更新されるたびに、レルム通知ブロックを使用してTableViewを更新しています。レルムの結果が更新されている間、TableViewがNSInternalInconsistencyExceptionをスローします

ただし、TableViewの更新中に新しい項目がRealmの結果に追加され、NSInternalInconsistencyExceptionが表示されることがあります。

私の環境:

シナリオ:

私はチャットメッセージを送信していると同時に、受信していると言うことができます。

  1. 私はRealmデータベースに送信したメッセージを追加しています。
  2. My Realm通知ブロックがTableViewの更新を開始します。
  3. 私は誰かからチャットメッセージを受け取ると同時に、レルムデータベースにも追加されます。
  4. レルムのサイズは更新の開始時に発生し、受信メッセージがレルムデータベースに追加されたため、更新の最後のサイズと異なるため、TableView更新が完了してNSInternalInconsistencyExceptionがスローされます。スローされ

例外:再現する

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. テーブルビューコントローラを作成します。
  2. realm.objects(ChatMessage.self)を使用していくつかの結果を得る。
  3. Realm collection notificationを使用してTableViewを更新します。
  4. 0.1秒間隔で新しいメッセージをRealmに追加します。
  5. アプリをビルドして実行します。
  6. 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

関連する問題