简体   繁体   中英

CoreData threading issue with Google Place Picker with unrelated Managed Object Context

Sorry for the long post; however, I wanted to be through.

I have multiple Managed Object Contexts (moc) and a substantial code working stable and consistently. Except for the current issue. Weird thing is the address of the moc is none of the ones I created - an unrelated moc is crashing that I am not even accessing. Please read on if you have time.

Very simply put, I crate a new child moc and pass it on to the new view controller. User sets some values and saves them. Child moc is saved, then parent. However, in the edit view controller, if the user selects to show the Google Place Picker, just as starting to show the window I get a multithreading error. I get this error on iPhone 6 Plus actual device, but not on iPhone 6S. It works with no problems whatsoever. With 6 Plus, always crashes at exactly the same place.

I've enabled com.apple.CoreData.ConcurrencyDebug 1

This is the main wiew that creates the child view for editing:

// =========================================================================
// 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)
    }
}

This is the editing view controller where the properties are declared:

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

This is where I call the show map function:

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

This is the google place picker where the code crashes exactly the same spot every time and I am not even accessing any contexts here (I was actually, but deleting them did not solve the problem):

// =========================================================================
// 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

        }
    }
}

I write everything to log, and the relevant info from the log are below. As you can see, I create 4 contexts, the addresses are shown, but the error is on some MOC that I have not created. Could it be that, GooglePlacePicker is using a moc of its own, and somehow getting mixed with mine :)???

[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:

I am using Xcode Version 8.1 beta (8T29o), Swift 2.3 but saw the same behavior with Xcode Version 7.3.1.

Any pointers are greatly appreciated.

The stack at break is shown below. It does not show which line the violation occurs. If it did, it would have been a lot easier to track down the bug.

CorData多线程违规

EDIT 1 - Save function

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)
        }
    }
}

You are calling -[NSManagedObjectContext save:] in the -[GMSTileDataCache storeCacheableTileDatas:completionHandler:] completion block. That is being executed on a background thread and I suspect you are calling save on a context that is not associated with that background thread.

Quick answer is to wrap that save in a -performBlockAndWait: .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM