【问题标题】:CoreData threading issue with Google Place Picker with unrelated Managed Object Context具有无关托管对象上下文的 Google Place Picker 的 CoreData 线程问题
【发布时间】:2016-09-25 10:25:11
【问题描述】:

抱歉,帖子太长了;但是,我想通过。

我有多个托管对象上下文 (moc) 和大量稳定且一致地运行的代码。除了当前的问题。奇怪的是 moc 的地址不是我创建的地址 - 一个不相关的 moc 正在崩溃,我什至无法访问。有时间请继续阅读。

简单地说,我创建了一个新的子 moc 并将其传递给新的视图控制器。用户设置一些值并保存它们。子 moc 被保存,然后是父级。但是,在编辑视图控制器中,如果用户选择显示 Google Place Picker,就像开始显示窗口一样,我会收到多线程错误。我在 iPhone 6 Plus 实际设备上收到此错误,但在 iPhone 6S 上没有。它没有任何问题。使用 6 Plus,总是在完全相同的地方崩溃。

我已启用com.apple.CoreData.ConcurrencyDebug 1

这是创建子视图进行编辑的主视图:

// =========================================================================
// EventsTableViewController
class EventsTableViewController: UITableViewController {
    // =========================================================================
    // MARK: - Target Action Methods
    @IBAction func didTapNewEvent(sender: UIBarButtonItem) {
        guard let vc = storyboard?.instantiateViewControllerWithIdentifier(EditEventTableViewController.identifier) as? EditEventTableViewController else { DLog("ERROR creating EditEventTableViewController"); return }

        // create a child context
        let eventContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
        eventContext.parentContext = context
        eventContext.name = "child_of_mainContext_for_Event"
        let event = NSEntityDescription.insertNewObjectForEntityForName(Event.entityName, inManagedObjectContext: eventContext)
        vc.segueObject = event
        vc.context = eventContext
        vc.shouldDismiss = true
        vc.delegate = self
        let nc = UINavigationController(rootViewController: vc)
        presentViewController(nc, animated: true, completion: nil)
    }
}

这是声明属性的编辑视图控制器:

// =========================================================================
// EditEventTableViewController
class EditEventTableViewController: UITableViewController {
    // =========================================================================
    // MARK: - Google Place properties
    private var context: NSManagedObjectContext!
    private var placePicker: GMSPlacePicker!
}

这是我调用显示地图功能的地方:

// =========================================================================
// MARK: - UITableViewDelegate
extension EditEventTableViewController {
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath == Cell.Location.indexPath {
            didTapSelectFromMap()
        }
    }
}

这是 google 地点选择器,其中代码每次都在完全相同的位置崩溃,我什至没有在这里访问任何上下文(我实际上是,但删除它们并没有解决问题):

// =========================================================================
// MARK: - Google Places
extension EditEventTableViewController {
    func didTapSelectFromMap() {
        guard let event = selectedObject as? Event else { DLog("ERROR object is not Event"); return }
        guard let location = locationManager.location else {
            DLog("Cannot get location, will try again")
            locationManager.startUpdatingLocation()
            return
        }

        DLog("current location: \(location)")

        // create viewport around where the user is
        let center = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
        let northEast = CLLocationCoordinate2DMake(center.latitude + 0.001, center.longitude + 0.001)
        let southWest = CLLocationCoordinate2DMake(center.latitude - 0.001, center.longitude - 0.001)
        let viewport = GMSCoordinateBounds(coordinate: northEast, coordinate: southWest)
        let config = GMSPlacePickerConfig(viewport: viewport)

        placePicker = GMSPlacePicker(config: config)

        placePicker.pickPlaceWithCallback {     // the code crashes here! But I have no idea where the violation occurs.
            (place, error) in

        }
    }
}

我将所有内容都写入日志,日志中的相关信息如下。如您所见,我创建了 4 个上下文,显示了地址,但错误出现在我尚未创建的某些 MOC 上。会不会是,GooglePlacePicker 正在使用它自己的 moc,并以某种方式与我的混在一起 :)???

[00059]:CDStack.swift                  :parentContext................. :12:41:18 CREATED <NSManagedObjectContext: 0x1741dbe40>: parent
[00068]:CDStack.swift                  :context....................... :12:41:18 CREATED <NSManagedObjectContext: 0x1741dbd50>: main
[00077]:CDStack.swift                  :importContext................. :12:41:18 CREATED <NSManagedObjectContext: 0x1701dd3d0>: import
[00095]:CDStack.swift                  :firebaseContext............... :12:41:21 CREATED <NSManagedObjectContext: 0x1741dc020>: firebase
[00127]:EditEventTableViewController.s :viewDidLoad()................. :12:43:48 Context: <NSManagedObjectContext: 0x1741de3c0>: child_of_mainContext_for_Event
[00375]:EditEventTableViewController.s :didTapSelectFromMap()......... :12:43:54 current location: <+78.675603,-93.352320> +/- 1414.00m (speed -1.00 mps / course -1.00) @ 25/09/2016, 12:43:54 Southern European Spring Time
2016-09-25 12:43:54.630499 App[1504:413029] [error] error: The current thread is not the recognized owner of this NSManagedObjectContext(0x1703c3de0).  Illegal access during objectRegisteredForID:

我使用的是 Xcode 8.1 beta (8T29o)、Swift 2.3,但在 Xcode 7.3.1 上看到了相同的行为。

非常感谢任何指针。

中断时的堆栈如下所示。它不显示违规发生在哪一行。如果是这样,那么追踪错误会容易得多。

EDIT 1 - 保存功能

class func save(moc:NSManagedObjectContext) {
    moc.performBlockAndWait {
        if moc.hasChanges {
            do {
                try moc.save()
            } catch {
                DLog("ERROR saving context '\(moc)' - \(error)")
            }
        } else {
            // there are no changes
        }
        if let parentContext = moc.parentContext {
            save(parentContext)
        }
    }
}

【问题讨论】:

    标签: swift multithreading core-data google-places-api


    【解决方案1】:

    您在-[GMSTileDataCache storeCacheableTileDatas:completionHandler:] 完成块中调用-[NSManagedObjectContext save:]。这是在后台线程上执行的,我怀疑您在与该后台线程无关的上下文中调用 save。

    快速的答案是将保存的文件包含在 -performBlockAndWait: 中。

    【讨论】:

    • 问题是我没有调用这些函数。这些位于 Google Place Picker 库中。我有一个名为CDStack 的单例,用于使用-performBlockAndWait 以递归方式保存托管对象上下文(如果有的话)。我在那里放了一个断点,它永远不会被调用。我开始认为违规行为在谷歌图书馆内部,上下文属于那里。这甚至可能吗?我已经编辑了代码以显示保存功能。我从不保存在其他任何地方。上面的didTapSelectFromMap() 是当时唯一正在执行的代码。
    • Google 代码很容易出现线程错误。我建议查看该方法的来源,并在可能的情况下对其进行更改以确认它可以纠正问题。
    • 我认为 Google 不会公开这些功能。我能想到的最好的测试方法是使用 Google Place Picker 建立一个非常简单的 Core Data 项目,然后调用 pickPlaceWithCallback,这似乎违反了线程。感谢您的意见,我很感激。
    猜你喜欢
    • 1970-01-01
    • 2021-07-12
    • 2011-04-10
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    • 2011-10-04
    • 2015-02-26
    • 1970-01-01
    相关资源
    最近更新 更多