I am seeing some very odd behavior with Core Data using Swift and Xcode 7.2. Below is an animation illustrating the issue.
I created a simple example with an entity named 'Person' that has only a 'name' attribute. I fetch all of the 'Person' entities and display them in a table view. If I force a cell to be reloaded then the textLabel that holds the name and NSManagedObject objectID displays without the name. The objectID, however, remains unchanged. This happens both on the device and in the simulator.
I'm printing the value of the Person entity to the console in "cellForRowAtIndexPath" so that I can show the value when the cell is initially loaded vs. when it's reloaded.
As you can see, it appears that the NSManagedObject is still present despite the fact that it seems to lose all references to its 'Person' subclass.
Here is the code for the affected ViewController:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let cellIdentifier = "PersonCell"
@IBOutlet weak var tableView: UITableView!
var people: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
people = fetchPeople()
}
func fetchPeople() -> NSArray {
let context = AppDelegate().managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Person")
let sort = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sort]
do {
let results = try context.executeFetchRequest(fetchRequest)
return results
} catch {
let error = error as NSError
print("\(error.domain) \(error.code): \(error.localizedDescription)")
return []
}
}
// MARK: - UITableView methods
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
let person = people[indexPath.row]
print("in cellForRowAtIndexPath:")
print(people[0])
cell.textLabel?.text = "\(person.name) - \(person.objectID)"
return cell
}
}
Any help is appreciated. Thank you.
The problem is here:
let context = AppDelegate().managedObjectContext
You create a new instance of the application delegate, and with it
a managed object context. As soon as program control returns from fetchPeople()
, there is no reference to the context anymore and it is
deallocated. The effect is that accessing properties of managed objects
which were created in this context returns nil
.
What you want is to get a reference to the (one and only) application delegate and its managed object context, something like
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
Alternatively, pass the context to the view controller in
the didFinishLaunchingWithOptions
method or in
prepareForSegue
or whatever is suitable in your app.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With