Reputation: 32061
I have a simple NSManagedObject
subclass:
@objc class MyModel: NSManagedObject {
@NSManaged var myProperty: String
}
However, the following code:
var model = NSEntityDescription.insertNewObjectForEntityForName("MyModel", inManagedObjectContext: managedObjectContext) as MyModel
assert(model != nil) // passes
if model.myProperty != nil { //crashes
println("not nil")
}
crashes at if model.myProperty != nil
with a EXC_BAD_ACCESS
. Why is this happening? This only started happening with Beta 5, and worked properly with Beta 4.
The above class was automatically generated using Xcode, so they did not add a ?
to the end of the property. However, manually adding a ?
to the end of the property does resolve the issue (@NSManaged var myProperty: String?
).
My question is, why doesn't Xcode automatically add the question mark to make it optional if it is marked as such in the schema, and why was this not an issue in previous betas?
Upvotes: 6
Views: 3184
Reputation: 13
It is very important to track where and how the properties are accessed. In my case my core data stack is build with one parent NSManagedObjectContext and temporary child NSManagedObjectContext, which are doing the writing and deleting on a background thread, with the guidance of this article.
In my case the problem was with one (unusually) complex operation involving few nested for loops and async calls to the back-end. Because of the way our CMS system works we can not fetch the data normally, so we will have to resolve to the nested loops. So, imagine a lobby page with different tabs of games. Each tab has sections of games. In order to populate it I am:
It is so obvious that it is easy to overlook, but after spending a day and a half in debugging I figured it out.
When I store the tab entities with the temporary NSManagedObject I am then creating a Task and I use the tab's identifier
property to fetch the games. Which unfortunately leads to crash because. But doing so I am introducing another thread in the picture and the app crashes.
Task.detached {
// `tab`'s MOC is created in a different thread and accessing it's properties can lead to a crash
let tabId = tab.tabId
}
Now, in order to fix this the safest way I managed to find is:
let tabId = tab.tabId
Task.detached {
...fetch the games using the `tabId` value
if let threadSafeTab = managedObjectContext.object(with: tab.objectID) as? GamesTab {
// Now the entity is synced properly and we can safely access it's properties
threadSafeTab.addToGamesSections(section)
}
}
For reference check Apple's documentation on object(with:)
I hope my answer is helpful :)
Upvotes: 0
Reputation: 845
To make it work you should do 2 things :
1) in the NSManagedObject
subclass add ? to made the property optional
@objc class MyModel: NSManagedObject {
@NSManaged var myProperty: String? // <-- add ? here
}
2) in your implementation as suggested in the previous answer
if let aProperty = model.myProperty? {
// do something with aProperty
}
Note that if you forgot to add ? in the NSManagedObject
subclass you we have compilation error.
Upvotes: 7
Reputation: 80265
Actually, the pattern Apple suggests in its Swift Programming Language guide is
if let aProperty = model.myProperty? {
// do something with aProperty
}
Upvotes: 0