简体   繁体   中英

CoreData Sort Malfunction

Below is the code I use to add a new item to my CoreData list. My goal is to insert the new item at the beginning, at position 0. Sometimes it works, other times it inserts it second from the top. Even if I have 5, 10, or more items, it never gets inserted randomly.

My problem is simply that after trying to debug the reason why the new item sometimes ends up second from the top, I'm only more confused. When I use "print()" to show the list contents after the insert/sort, sometimes I see two of the new item. Yet, I've never seen two appear appear in the view.

My research doesn't help either. This tutorial shows doing the CoreData save-context, THEN appending the new record.

func saveName(name : String) {
let appDelegate    = UIApplication.sharedApplication().delegate as? AppDelegate
let managedContext = appDelegate!.managedObjectContext
let entity         = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedContext)

let person         = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)

person.setValue(name, forKey:"name")

do
{
    try managedContext.save()
}
catch
{
    print("There is some error.")
}

cityNames.append(person)
}

Why?! Wouldn't that mean the new record isn't saved?

My code to insert the new item and resort the list:

func addToList (sender: AnyObject, entity: String) {
    //Setup CoreData context
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!

    let entityVar =  NSEntityDescription.entityForName(entity, inManagedObjectContext: managedContext)
    let task = NSManagedObject(entity: entityVar!, insertIntoManagedObjectContext:managedContext)

    //Assign received cell to 'cell'
    let cell = sender as! CustomTableViewCell

    //Get sorted CoreData list and assign it to targetList_Cntxt
    let fetchRequest = NSFetchRequest(entityName: entity)
    let sortDescriptor = NSSortDescriptor(key: "displayOrder", ascending: true)
    fetchRequest.sortDescriptors = [ sortDescriptor ]
    do {
        let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]

        if let results = fetchedResults {
            targetList_Cntxt = results
        }
    } catch { print(error) }

    //Prepare the cell's values to be added by setting its contents to variables
    let nameValue = cell.nameLabel!.text
    let descValue = cell.descLabel!.text

    //Set cell name/desc variables to the NSObject "task"
    task.setValue(nameValue, forKey: "name")
    task.setValue(descValue, forKey: "desc")

    //Insert, update sort order of both lists, save
    let insertAt = 0
    targetList_Cntxt.insert(task, atIndex: insertAt)
    update_TargetDisplayOrder() //Destination list
    do {
        try managedContext.save()
    } catch { print(error) }
    print(targetList_Cntxt)
}

func updateDisplayOrder() {
    for i in 0..<taskList_Cntxt.count {
        let task = taskList_Cntxt[i]
        task.setValue( i, forKey: "displayOrder" )
    }
}

It doesn't look like your code is 'thread safe', causing unexpected behaviour. Specifically here:

//Insert, update sort order of both lists, save
let insertAt = 0
targetList_Cntxt.insert(task, atIndex: insertAt)
update_TargetDisplayOrder() //Destination list
do {
    try managedContext.save()
} catch { print(error) }
print(targetList_Cntxt)

Try:

managedContext.performBlockAndWait() {
    let insertAt = 0
    targetList_Cntxt.insert(task, atIndex: insertAt)
    do {
        try managedContext.save()
    } catch { print(error) }
}

print(targetList_Cntxt)
update_TargetDisplayOrder() //Destination list

This ensures that the block of code is completed first (the insertion and save). Therefore when you call update_TargetDisplayOrder , the data has been properly saved.

Ideally you would want to be doing this on the privateQueue, and not on the main thread. When working with CoreData, make sure you enable multi-threading assertions. See http://oleb.net/blog/2014/06/core-data-concurrency-debugging/

In the first example:

This tutorial shows doing the CoreData save-context, THEN appending the new record. ... Why?! Wouldn't that mean the new record isn't saved?

Don't confuse adding the new object to an array with saving it to the store. This line:

let person = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)

creates a new NSMO and inserts it into the context. When you call

try managedContext.save()

the context writes any changes (including the newly inserted NSMO) to the store. The subsequent:

cityNames.append(person)

just adds the NSMO to an array - it is unrelated to the actual process of saving the NSMO.

In your own code, this line creates a new object and inserts it into the context:

let task = NSManagedObject(entity: entityVar!, insertIntoManagedObjectContext:managedContext)

At this point it has not been saved to the store, but it exists within the context. When you run your fetch:

do {
    let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
    if let results = fetchedResults {
        targetList_Cntxt = results
    }
}

the context fetches NSMOs from the store, but it also includes NSMOs that have been created but not yet saved - so fetchedResults (and consequently targetList_Cntxt ) will include the new item task . When you then have:

targetList_Cntxt.insert(task, atIndex: insertAt)

you are adding it to the targetList_Cntxt again . Hence the duplicates. Without seeing your table view datasource methods, I can't be sure why these duplicates don't appear in the view, but my guess is that the TV is populated by a fresh fetch, rather than using targetList_Cntxt.

A quick fix will be to move:

let task = NSManagedObject(entity: entityVar!, insertIntoManagedObjectContext:managedContext)

to just after the fetch, so task will not be included in the fetch. Your later code will then insert it at the correct index.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM