[NSDecimalNumber retain]: message sent to deallocated instance 0x174222220, but why? - ios

I seem to be getting this error.
*** -[NSDecimalNumber retain]: message sent to deallocated instance 0x174222220
The first time I run the app, the code executes fine, but if I got back to that VC, the app crashes with the message above. I did some research and I think it means that some how that NSDecimal is being released. It's very odd, as I have 3 other decimals all set up the exact same way that are working.
The decimals are being stored in Core Data, and then being set to a label in my VC in side the cellForIndexAt method.
print("\(historyEntry.newAmount) new amount")
The very first time I get back the correct amount. But the second time or if I try move the tableview the app crashes with the message above. What would cause the decimal to release itself?
EDIT
I have a CoreDataStack swift file and the managedContext is being created like this:
lazy var managedContext: NSManagedObjectContext = {
return self.storeContainer.viewContext
}()
I am fetching the object like this:
// load the data
let fetchRequest: NSFetchRequest<Statement> = Statement.fetchRequest()
fetchRequest.predicate = NSPredicate(format:"person.name == %# AND amountOwed >= 0", personName)
let sort = NSSortDescriptor(key: #keyPath(Statement.amountOwed), ascending: true)
fetchRequest.sortDescriptors = [sort]
positiveFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.managedContext, sectionNameKeyPath: nil, cacheName: nil)
do{
try positiveFetchedResultsController.performFetch()
}catch let error as NSError{
print("Fetching error: \(error), \(error.userInfo)")
}
positiveFetchedResultsController.delegate = self
I think pass the fetched object that I am using to another ViewController and access its properties like this:
print("\(historyEntry.changeAmount) change amount") // gives me back the correct amount that was saved evrytime.
This attribute however crashes after the first time
print("\(historyEntry.newAmount) new amount") // first time correct, after that error message from above.
EDIT
Here is the CoreDataStack class
import Foundation
import CoreData
class CoreDataStack {
private let modelName: String
init(modelName: String) {
self.modelName = modelName
}
lazy var managedContext: NSManagedObjectContext = {
return self.storeContainer.viewContext
}()
private lazy var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: self.modelName)
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
}
return container
}()
func saveContext () {
guard managedContext.hasChanges else { return }
do {
try managedContext.save()
} catch {
let nserror = error as NSError
print("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}

What are the names of your NSDecimalNumber properties? If any of the property names start with "new", that will confuse ARC and lead to problems like this.
To quote from Apple's Introduction to ARC,
You cannot give an accessor a name that begins with new. This in turn
means that you can’t, for example, declare a property whose name
begins with new unless you specify a different getter.

I would try and make sure that the managed object context is not being deallocated and reallocated when you reenter your VC. That could explain why your MOCs are deallocated.
Other than that, I would enable "Zombie Objects" under Product/Scheme/Edit Scheme/Run/Diagnostics and see if that provides some more hints.

Related

How to use NSEntityDescription with a project that was not created with use CoreData checked

I am trying to save high score data for users that play my card game. I want to use CoreData to save and load that data but I am very new to swift programming and CoreData in general and my project was created well before I knew I wanted to use CoreData and the easiest way to import it is to check the 'use CoreData' box. So here is my ViewController code (only the important parts of course)
var trackTapCount = 0
private var managedContext:NSManagedObjectContext!
private var entityDescription:NSEntityDescription!
private var scoreData: NSManagedObject!
var capturedData = [Score]()
var storedInitials: String = ""
var storedTime: String = ""
var sessionData = Score(username: "", completionTime: 0, numTurns: 0, dateTimestamp: "")
override func viewDidLoad() {
super.viewDidLoad()
let saveAppDelegate = UIApplication.shared.delegate as? AppDelegate
managedContext = saveAppDelegate?.persistentContainer.viewContext
// managedContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.newBackgroundContext()// Does this work?
entityDescription = NSEntityDescription.entity(forEntityName: "ScoreData", in: managedContext)
//
// scoreData = NSManagedObject(entity: entityDescription, insertInto: managedContext)
}
My AppDelegate code
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "BoschBrooke_4.1_MemoryGame")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
And finally, my Entity setup looks like the following:
E: ScoreData
Attributes:
initials Type: String
tapCount Type: Integer 16
timeComplete Type: String
timestamp Type: String
Any idea how I can use the NSEntityDescription and NSManagedObject to save my data properly? One of the big issues I am running into is at the entityDescription line in my ViewController. It's saying 'unexpectedly found nil while implicitly unwrapping an Optional value'.

CoreData crashes when fetching data in loop

I'm working with old xcdatamodel, it was created in xcode 7.3 (that's a crucial since I don't have the following issue on modern models). At the same time, this issue is not cured by simple changing Tool Version to Xcode 9.0 for my xcdatamodel.
I'm fetching data in for loop, in the thread of context I use for fetching data. When I try to fetch the entity that has already been fetched once, coreData crashes with EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP). Zombie tracking says [CFString copy]: message sent to deallocated instance 0x608000676b40.
This is the concept of what I do:
LegacyDatabaser.context.perform {
do {
for _ in 0..<10 {
let entity = try self.legacyDatabase.getEntity(forId:1)
print(entity.some_string_property) // <- crash here
}
} catch {
// ...
}
}
Here is the context initializer:
class LegacyDatabaser {
static var context: NSManagedObjectContext = LegacyDatabaseUtility.context
// ...
}
And
class LegacyDatabaseUtility {
fileprivate class var context: NSManagedObjectContext {
//let context = NSManagedObjectContext(concurrencyType:.privateQueueConcurrencyType)
//context.persistentStoreCoordinator = storeContainer.persistentStoreCoordinator
//return context // This didn't help also
return storeContainer.newBackgroundContext()
}
private static var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name:"MyDBName")
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
return container
}()
}
Here is the data fetcher:
func getEntity(forId id: NSNumber) throws -> MyEntity? {
// Create predicate
let predicate = NSPredicate(format:"id_local == %#", id)
// Find items in db
let results = try LegacyDatabaseUtility.find(predicate:predicate, sortDescriptors:nil, in:LegacyDatabaser.context)
// Check it
if results.count == 1 {
if let result = results.first as? MyEntity {
return result
} else {
return nil
}
} else {
return nil
}
}
And:
static func find(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, in context: NSManagedObjectContext) throws -> [NSManagedObject] {
// Create a request
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName:"MyEntity")
// Apply predicate
if let predicate = predicate {
fetchRequest.predicate = predicate
}
// Apply sorting
if let sortDescriptors = sortDescriptors {
fetchRequest.sortDescriptors = sortDescriptors
}
// Run the fetchRequest
return try context.fetch(fetchRequest)
}
I don't address the context somewhere in a parallel, I'm sure I use the correct thread and context (I tested the main context also, the same result). What I'm doing wrong, why re-fetching the same entity fails?
If anyone catches any quirk crashes like one I described above, check the name of properties in your NSManagedObject. In my case, crashes were on call new_id property which is, I guess, kinda reserved name. Once I renamed this property, the crashes stopped.

How To Sort FetchedResultsController By Measurement Foundation Class, Formerly: Core Data Crash While Saving

I am having a strange crash while saving new data into Core Data. The relevant portions of the structure are as follows:
struct ParachuteDataController {
let databaseController = DatabaseController()
let context = DatabaseController().getContext()
func addParachute(newParachute: ParachuteData) {
let parachute = Parachute(context: context)
parachute.setValue(newParachute.diameter, forKey: CoreData.diameter)
parachute.setValue(newParachute.area, forKey: CoreData.area) //Measurement
parachute.setValue(newParachute.dragCoefficient, forKey: CoreData.dragCoefficient) //Measurement
parachute.setValue(newParachute.mass, forKey: CoreData.mass) //Measurement
parachute.setValue(newParachute.material.rawValue, forKey: CoreData.material) //String
parachute.setValue(newParachute.measurementType.rawValue, forKey: CoreData.measurementType)
parachute.setValue(newParachute.shape.rawValue, forKey: CoreData.shape) //String
parachute.setValue(newParachute.vented, forKey: CoreData.vented) //Bool
if newParachute.vented {
parachute.setValue(newParachute.ventDiameter, forKey: CoreData.ventDiameter)//Optional Measurement
}
saveInCoreData()
}
private func saveInCoreData() {
do {
try DatabaseController().getContext().save()
} catch let error {
print(error)
}
}
}
class DatabaseController: NSObject {
static let sharedInstance = DatabaseController()
override init() {}
public func getContext() -> NSManagedObjectContext {
return DatabaseController.sharedInstance.persistentContainer.viewContext
}
public var fetchPredicate : NSPredicate?
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Recovery")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
CoreData is simply a constant Struct.
My model has attributes for each of the listed items in addParachute. When I try to add all of the items to the Core Data model, I get the following crash:
NSManagedObjectContextObjectsDidChangeNotification. -[NSMeasurement compare:]: unrecognized selector sent to instance 0x1c04211e0 with userInfo (null)
2018-04-03 08:28:48.533419-0400 Recovery[1210:643911] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSMeasurement compare:]: unrecognized selector sent to instance
If I pull out any ONE item of the attributes, it saves fine. It does not matter which item it is. I can't even narrow it down to any particular attribute as a result. I have other entities which save measurements, set up essentially identically and there are no problems.
Suggestions? Thanks in advance.
Edit:
I am on the right track as to the actual cause of my crash thanks to this post:
Extremely Odd Crash When saving a transformable NSAttributedString into Core Data.
I hadn't found it earlier as it is an Objective C question. However, the underlying issue is the same: my FetchedResultsController updating as a result of the Core Data save.
In my View Controller class, I have the following function:
func configureFetchedResultsController() {
let context = databaseController.getContext()
let parachutesFetchRequest = NSFetchRequest<Parachute>(entityName: CoreData.parachute)
let primarySortDescriptor = NSSortDescriptor(key: CoreData.material, ascending: true)
let secondarySortDescriptor = NSSortDescriptor(key: "diameter.value", ascending: true)
let sortDescriptor = [primarySortDescriptor, secondarySortDescriptor]
parachutesFetchRequest.sortDescriptors = sortDescriptor
self.fetchedResultsController = NSFetchedResultsController<Parachute>(
fetchRequest: parachutesFetchRequest,
managedObjectContext: context,
sectionNameKeyPath: CoreData.material,
cacheName: nil)
self.fetchedResultsController.delegate = self
}
The cause of the crash is let secondarySortDescriptor = NSSortDescriptor(key: "diameter", ascending: true). diameter is a Measurement and this does not seem to play well with the FRC's sorting. When I changed the code to let secondarySortDescriptor = NSSortDescriptor(key: "diameter.value", ascending: true) in hopes of having it sort by the value, I get the following error:
NSManagedObjectContextObjectsDidChangeNotification. [ valueForUndefinedKey:]: this class is not key value
coding-compliant for the key value. with userInfo {
NSTargetObjectUserInfoKey = " value: 0.304800 unit: m";
NSUnknownUserInfoKey = value; }
Is it possible to sort by a Measurement and, if so, how?
Thanks for all who provided suggest that helped me narrow the issue.
As I indicated in the edit, the issue had nothing to do with the Core Data save. The crash is caused once the save occurs and the FetchedResultsController(frc) is updated. It is resorted based on my sortDescriptors, one of which is a Measurement. While I have not been able to determine what, if any, syntax is necessary to sort on the Measurement, sorting on another attribute in Core Data prevents the crash. For someone looking into this in the future, I tried sorting on the Measurement itself and the value of the measurement, both of which caused runtime crashes. So, I am marking this question as solved for my initial question.
If you get a crash due to a compare statement, look to your frc as the cause.

Fatal Error When Trying To Save Core Data Objects Via Relationship

I am adding a newly created user exercise to an existing user routine via a save command. To explain the code below, I set the objectcontext, then is the exercise is new (userExercise is nil) I execute a new save block.
I then check if this new exercise is being added to an existing routine (associatedRoutineToAddTo is a string routine name from the previous VC).
This is where the issue is. I attempt to get the UserRoutine object that it needs to be added to, using a predicate based on the routines name from the string i passed from the previous VC. then i attempt to add the exercise to the routine. This causes the crash.
The rest of the code isnt too relevant as it works fine in terms of saving an edit or new exercise with no parent routine, its just this one part.
Here is what I am trying:
func getMainContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
func createExercise() {
print("SAVE EXERCISE PRESSED")
let managedObjectContext = getMainContext()
if userExercise == nil {
print("SAVING THE NEW EXERCISE")
let newUserExercise = UserExercise(context: self.managedObjectContext!)
newUserExercise.name = userExerciseName.text
newUserExercise.sets = Int64(userSetsCount)
newUserExercise.reps = Int64(userRepsCount)
newUserExercise.weight = Double(self.userExerciseWeight.text!)!
newUserExercise.dateCreated = NSDate()
if self.associatedRoutineToAddTo != nil {
let existingUserRoutine = UserRoutine(context: managedObjectContext)
let request: NSFetchRequest<UserExercise> = UserExercise.fetchRequest()
request.predicate = NSPredicate(format: "usersroutine.name == %#", self.associatedRoutineToAddTo!)
existingUserRoutine.addToUserexercises(newUserExercise)
do {
try managedObjectContext.save()
} catch {
fatalError("Failure to save context: \(error)")
}
} else if self.associatedRoutineToAddTo == nil {
print("THIS IS A FRESH EXERCISE WITHOUT A PARENT ROUTINE")
}
}
The error reads:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a relationship 'usersroutine' between objects in different contexts (source = <UserExercise: 0x618000280e60>
edit: revised my fetch code for review:
let fetchRequest: NSFetchRequest<UserRoutine> = UserRoutine.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "usersroutine.name == %#", self.associatedRoutineToAddTo!)
do {
existingUserRoutine = try managedObjectContext.fetch(fetchRequest) as! UserRoutine
print("Routine Below Fetched")
print(existingUserRoutine)
} catch {
print("Fetch Failed")
}
existingUserRoutine.addToUserexercises(newUserExercise)
You create the object here:
let newUserExercise = UserExercise(context: self.managedObjectContext!)
And fetch the related object here:
let existingUserRoutine = UserRoutine(context: managedObjectContext)
You've created a local variable called managedObjectContext here:
let managedObjectContext = getMainContext()
And your error states:
attempt to establish a relationship 'usersroutine' between objects in different contexts
Therefore, your property managedObjectContext is not the same as that returned by getMainContext()
In addition to all that, you're creating a brand new UserRoutine, and assigning it to a value called existingRoutine, then creating a fetch request that you don't do anything with, which suggests you're a little confused about what is supposed to be happening here.
WHen you make UserExcercise you are referencing a context saved on your VC:
let newUserExercise = UserExercise(context: self.managedObjectContext!)
Then
let existingUserRoutine = UserRoutine(context: managedObjectContext)
The getMainContext() acquire context here?

Saving text from a UITextView using core data Swift

Completely new to iOS Development and Swift so I hope you'll bear with me. I'm creating a simple note app based off the apple Notes app.
I am also completely new to core data. What I'm trying to do is save the text from the UITextView. Strangely enough this works but only after I remove this piece of code,
if let detail: Note = self.detailItem {
if let textView = self.textView {
textView.text = detail.noteText
}
create a new note, then reintroduce the code...
func configureView() {
// Update the user interface for the detail item.
if let detail: Note = self.detailItem {
if let label = self.detailDescriptionLabel {
label.text = detail.timeStamp.description
}
if let detail: Note = self.detailItem {
if let textView = self.textView {
textView.text = detail.noteText
}
}
}
For a note that has already been created, this code is able to save the text in the textView. If I try to create a new note and then open it, the code above will crash the app.
This error comes up: Thread 1: EXC_BAD_ACCESS(code=1, address=0x0)
I think I have made a silly mistake like not initializing my model "Note.swift" class.
I'm using the boilerplate code for a master detail app with core data. I also have two "insertNewObject" functions. One is in the MasterViewController and the other is in the DetailViewController.
DetailViewController.swift
func insertNewObject(sender: AnyObject) {
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as! NSManagedObject
Here's the full code including the fetchedResultsController.
func insertNewObject(sender: AnyObject) {
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as! NSManagedObject
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
newManagedObject.setValue(self.textView.text, forKey: "noteText")
newManagedObject.setValue("I changed", forKey: "noteTitle")
// Save the context.
var error: NSError? = nil
if !context.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
// MARK: - Fetched results controller
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("Note", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(animated: Bool) {
detailItem!.noteText = self.textView.text
detailItem!.noteTitle = "I changed"
var error: NSError? = nil
if !self.managedObjectContext!.save(&error) {
abort()
}
}
I was able to figure it out. The issue was that I didn't set a value for "noteText" when I first called func insertNewObject(sender: AnyObject). This resulted in a nil being set to textView.text and therefore crashing the app. Thanks for your help #BaseZen!

Resources