CoreData Crash -objc_release & objc_msgSend

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

  1. HomeVC > buttonPress immediately push on UnsavedVC
  2. UnsavedVC (cells correctly load) > backButtonPressed immediately pop back to HomeVC
  3. HomeVC > buttonPress immediately push on UnsavedVC
  4. 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
}

Accepted Reply

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.

Replies

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.