I have several properties inside my CoreData Entity file named UnsavedModel, here are some of them.
I created my own file with a CollectionView and when I instantiate the newBodyText
and newHttpsStr
properties, it crashes. The correct values are being fed into both of the properties, which are just Strings, but it only crashes on those properties, none of the other properties. If I don't give those properties a value, everything works fine. What's the issue here?
and also
The problem occurs in this order
- HomeVC > buttonPress immediately push on UnsavedVC
- UnsavedVC (cells correctly load) > backButtonPressed immediately pop back to HomeVC
- HomeVC > buttonPress immediately push on UnsavedVC
- CRASH
FYI I have no problems writing to those properties. But when I do try to delete those 2 properties, it crashes, but have no problem deleting any of the other properties.
I've also gotten a crash on Thread 1 Queue : com.apple.main-thread (serial)
and objc_msgSend
for the same push/pop/push issue.
code:
class UnsavedController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var datasource = [CopyCoreDataModel]()
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(false, animated: false)
}
func fetchData() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let context = appDelegate.persistentContainer.viewContext
// I also tried context.performAndWait { ... run the do-try-catch fetchRequest in here... }
let fetchRequest: NSFetchRequest<UnsavedModel> = UnsavedModel.fetchRequest()
do {
let results = try context.fetch(fetchRequest)
for result in results {
guard let id = result.value(forKey: "id") as? String else { continue }
let isContained = tableData.contains(where: { $0.id ?? "" == id })
if !isContained {
let copy = CopyCoreDataModel(id: id, unsavedModel: result)
datasource.append(copy)
}
}
collectionView.reloadData()
} catch {
print(error)
}
}
}
// CopyCoreDataModel is necessary because I run some functions based on certain properties
class CopyCoreDataModel {
var fileUrl: URL?
var id: String?
var unsavedModel: UnsavedModel?
var postDate: Double?
// otherProperties of type String, Double, and Boolean
var micUrl: String?
var newBodyText: String?
var newHttpsStr: String?
init(id: String, unsavedModel: UnsavedModel) {
self.id = id
self.unsavedModel = unsavedModel
// self otherProperties = unsavedModel.otherProperties // run some function on some of the other properties. These all work perfectly fine
if let micUrl = unsavedModel.micUrl { // works perfectly fine
self.micUrl = micUrl
// function to get micURL from FileManager that eventually sets self.fileUrl
}
if let newBodyText = unsavedModel.newBodyText { // crash occurs here only if it has a value
self.newBodyText = newBodyText
}
if let newHttpsStr = unsavedModel.newHttpsStr { // crash occurs here only if it has a value
self.newHttpsStr = newHttspStr
}
}
}
func writeData(micURL: URL) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let entity: NSEntityDescription = NSEntityDescription.entity(forEntityName: "UnsavedModel", in: context)!
let object: NSManagedObject = NSManagedObject(entity: entity, insertInto: context)
object.setValue(UUID().uuidString, forKey: "id")
object.setValue(micURL.path, forKey: "micUrl")
object.setValue("abc", forKey: "newBodyText")
object.setValue("https...", forKey: "newHttpsStr")
// set other properties
do {
try context.save()
} catch let error as NSError {
print("could not save . \(error), \(error.userInfo)")
}
}
I also setup a CoreDataManager sharedInstance class and accessed the context through there let context = CoreDataManager.sharedInstance.persistentContainer.viewContext
but the same issue occurs
I also changed the CopyCoreDataModel's initializer to use a dict of the k/v from the unsavedModel but the crash still occurs
class CopyCoreDataModel {
// same exact properties
init(id: String, dict: [String: Any]) {
// set the properties using the values from the dict
}
}
func fetchData() {
let context = // ...
let fetchRequest = // ...
do {
let results = try context.fetch(fetchRequest)
for result in results {
guard let id = result.value(forKey: "id") as? String else { continue }
let isContained = datasource.contains(where: { $0.id ?? "" == id })
if !isContained {
let dict = createDictFromUnsavedModel(unsavedModel: result)
let copy = CopyCoreDataModel(id: id, dict: dict)
datasource.append(copy)
}
}
collectionView.reloadData()
} catch {
}
}
func createDictFromUnsavedModel(unsavedModel: UnsavedModel) -> [String:Any] {
var dict = [String: Any]()
// set dict using k/v from unsavedModel
return dict
}
Wow, who would've thought ...
The issue was both names began with new
as in newBodyText and newHttpsStr. That's not allowed. I found the answer here by @Silfverstrom -https://coderedirect.com/questions/397168/cfstring-release-message-sent-to-deallocated-instance
From Apple documentation -https://developer.apple.com/library/ios/releasenotes/objectivec/rn-transitioningtoarc/introduction/introduction.html-:
To allow interoperation with manual retain-release code, ARC imposes a constraint on method naming:
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
Once I changed them from newBodyText and newHttpsStr to updatedBodyText and updatedHttpsStr the crash went away.