iosswiftcore-datansmanagedobjectnspersistentstore

NSPersistentStoreCoordinator throws EXC_BAD_ACCESS when deallocating an NSManagedObject after reading a specific NSManagedProperty


Error Description

I have an app with some data stored locally using core data (backed by SQLite) and the following NSManagedObject subclass

import CoreData

@objc(ScoutingEventData)
class ScoutingEventData: NSManagedObject {
    @NSManaged var id: String?
    @NSManaged var type: String?
    @NSManaged var entityId: String?
    @NSManaged var oldStateJson: NSData?
    @NSManaged var newStateJson: NSData?
    @NSManaged var eventDate: NSDate?

    func toInsertEvent() throws -> ScoutingEvent.Insert {
        guard let id = id else { fatalError("events should have an event id") }
        guard let data = newStateJson else { fatalError("insert event should have newStateJson stored") }
    
        // If I uncomment this line, the error goes away. 
        // Somehow by ensuring that data never gets deallocated, the error never occurs.
        // globallllll = data
  
        return ScoutingEvent.Insert(id: id, entity: try ScoutingEntity.from(data))
    }
}

// debugging var to prevent data from being deallocated
var globallllll: NSData?

As mentioned in the comment in the snippet above, there is an error occurring if I allow the value I read out of the newStateJson property to be deallocated.

The error I receive comes from a background thread:

bad access

If I enable zombies in the diagnostic tool, I get this instead

enter image description here

And I also get the following message in the console if zombies are enabled:

2016-11-18 16:26:13.773 ScoutingData_Example[51750:4716636] *** -[CFData release]: message sent to deallocated instance 0x7f8c4eb10ae0

And the following stack trace:

enter image description here


What I've Tried So Far

I have tried storing the NSManagedObjectContext which was used to fetch this data in a static variable to ensure that the context never gets deallocated, but that had no effect.

I have tried converting the NSData? properties to String? properties, and storing the data as a Base64 encoded string instead of binary data (and also updating the backing model), but that had no effect either. The error still persisted.

I have tried commenting out the code that reads the property, and the error dissappeared, but that is obviously not an acceptable solution.

I have tried storing the value in a global variable after reading it, to prevent it from ever being deallocated, and the error dissappeared, but that is also not a valid solution.

I have tried commenting out all the code that uses the value, and instead only printing the value to the console, and the error persisted. This leads me to believe that the act of accessing the property at all, and then deallocating it later, is what creates the conditions for this error.


I am pretty stumped. It seems really odd that it should matter what I do with the data after I read it, and it seems especially odd that anything happens on a background thread when it gets deallocated.

What's even more odd is that it seems specific to this one property. For instance, the id property which I read right above the line that reads the newDataJson property doesn't cause any problems. You might think thats different because the id is a string, not NSData, but I tried converting my NSData property to a string property instead, and it still didn't change the error.

Any ideas would be greatly appreciated. Thanks.

Edit

Not sure if this will help, but here's my model schema

enter image description here


Solution

  • Give the property a name that doesn't start with new. I have had similar problems and had to rename my property to something like theNewState. I think the new prefix has special meaning and confuses ARC memory management.

    Edit: reference Apple ARC release notes, see the section saying "You cannot give an accessor a name that begins with new." So it's actually the accessor/getter name, not the property name itself.