"从不正确的线程"访问领域dispatch_group_notify块抛出的异常

时间:2015-12-14 03:36:41

标签: swift swift2 realm grand-central-dispatch

我已阅读其他答案,但无法找到合适的解决方案。

我有一个产品只有在属于该产品的所有IMAGES上传完成后才会上传到服务器。产品的细节(连同图像)被填充在视图控制器1上,然后他被带到下一个屏幕(视图控制器2),无论是否所有图像都已完成上传。 MY VC1完成了这样的产品上传过程。

let areAllImagesUploaded = RetailProductsService.sharedInstance.checkProductImageDependency(realm!, uuid: retailProduct.getClientId())

if areAllImagesUploaded {
    uploadProductToServer(retailProduct)
} else {
    do {
        try realm = Realm()
        RetailProductsService.sharedInstance.updateSyncStatusForSellerProduct(realm!, clientId: retailProduct.getClientId(), syncStatus: ProductSyncStatus.SYNC_FAILED)
        let groupT = dispatch_group_create()

        for sellerSKU in retailProduct.sellersSKUs {
            for productImage in sellerSKU.productImages { 
                dispatch_group_enter(groupT)
                let imageUploadInfo = ImageUploadInfo(productId: retailProduct.getClientId(), imageId: sellerSKU.getId(),imageData: productImage.imageData, uploadURL: ServerConfig.RETAIL_SERVER_UPLOAD_FILE_URL)
                ImageUploadManager.sharedInstance.queueImageForUpload(imageUploadInfo, completion: { (success, error) -> Void in
                    dispatch_group_leave(groupT)
                })

                dispatch_group_notify(groupT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
                    //self.uploadProduct(retailProduct)
                    self.uploadProductToServer(retailProduct) // Fails here
                })

            }
        }
    } catch {
        print("Error in saving product.")
    }
}

我已经标记了我收到此错误的行。我的应用程序已移至下一个视图控制器,而视图控制器1中的此功能继续上传图像,并且只要将与产品关联的所有图像上载到服务器,它就会尝试上传产品。但是,这个例外就失败了。

Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread'

请帮忙!

2 个答案:

答案 0 :(得分:1)

无法从不同的线程访问Realm对象。您通过threadX创建或从Realm存储中获取retailProduct,而不是通过调用dispatch_group_notify切换到其他某个线程(threadY)。要修复异常,您可能需要执行以下操作: 我假设您的retailProduct对象的类型为 RetailProduct ,并且在Realm存储中将 id 属性用作主键。当然,您可以使用其他查询来获取retailProduct

let retailProductId = retailProduct.id
dispatch_group_notify(groupT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
       // threadY executing this lines
       if let retailProduct = realm.objectForPrimaryKey(RetailProduct.self, key: retailProductId){
          self.uploadProductToServer(retailProduct) 
       }
 })

答案 1 :(得分:1)

我们需要了解不能从不同的线程访问Realm对象这一事实。这意味着什么以及如何解决这个问题。

首先,realm对象无法从不同的线程访问意味着,在一个线程中定义的一个线程实例无法从不同的线程访问。我们应该做的实际上是我们需要为每个线程提供不同的realm实例实例。

例如。让我们看一下例如我们在按钮点击后在后台线程中异步插入数据库中的50条记录,我们在主线程中添加通知块以更新计数标签中的人数。每个线程(主要和后台)都有自己的领域对象实例来访问Realm数据库。因为Realm Database是线程安全的。

class Person: Object {
    dynamic var name = ""
    convenience init(_ name: String) {
        self.init()
        self.name = name
    }
}


override func viewDidAppear(_ animated: Bool) {
    let realmMain = try!  Realm ()
    self.people = realmMain.objects(Person.self)
    self.notification = self.people?.addNotificationBlock{ [weak self] changes in
        print("UI update needed")
        guard let countLabel = self?.countLabel else {
            return
        }
        countLabel.text = "Total People: \(String(describing: self?.people?.count))"
    }
}

@IBAction func addHandler(_ sender: Any) {
    print(#function)
    let backgroundQueue = DispatchQueue(label: "com.app.queue",
                                        qos: .background,
                                        target: nil)



    backgroundQueue.async {
        print("Dispatched to background queue")
        let realm = try! Realm()
        try! realm.write {
            for i in 1..<50 {
                let name = String(format: "rajan-%d", i)
                //print(#function, name)
                realm.add(Person(name))
            }
        }

    }
}