简体   繁体   English

如何使用Swift使用Core Data更新/保存和保留非标准(可转换)属性?

[英]How can I get non-standard (transformable) attributes to update/save and persist using Core Data with Swift?

I've constructed a very basic example to demonstrate the issue I'm having attempting to update a transformable type and get the change to persist between app restarts. 我已经构建了一个非常基本的示例来演示我尝试更新可转换类型并在应用程序重新启动之间保持更改的问题。

I have an entity of type Destination ... 我有一个Destination类型的实体...

import Foundation
import CoreData

class Destination: NSManagedObject {
    @NSManaged var name: String
    @NSManaged var location: Location
}

... that has a simple name attribute (of type String) and an attribute of type Location : ...具有简单的名称属性(String类型)和类型为Location的属性:

import Foundation

class Location: NSObject, NSCoding {
    var address: String
    var latitude: Double
    var longitude: Double

    required init?(coder aDecoder: NSCoder) {
        address = aDecoder.decodeObjectForKey("Address") as? String ?? ""
        latitude = aDecoder.decodeObjectForKey("Latitude") as? Double ?? 0.0
        longitude = aDecoder.decodeObjectForKey("Longitude") as? Double ?? 0.0
        super.init()
    }

    init(address: String, latitude: Double, longitude: Double) {
        self.address = address
        self.latitude = latitude
        self.longitude = longitude

        super.init()
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(address, forKey: "Address")
        aCoder.encodeObject(latitude, forKey: "Latitude")
        aCoder.encodeObject(longitude, forKey: "Longitude")
    }
}

Location is configured as "transformable" in Core Data, because it has structure that none of the other basic types can handle. 位置在Core Data中配置为“可转换”,因为它具有其他基本类型都无法处理的结构。

Using Apple's boilerplate Core Data code, here's a view controller that simply does the following: 使用Apple的样板核心数据代码,这里是一个视图控制器,它只执行以下操作:

  • Gets the necessary appDelegate / ManagedApplicationContext references 获取必要的appDelegate / ManagedApplicationContext引用
  • Fetches a destination if one exists, creates one if not 如果存在目的地,则获取目的地,否则创建一个目的地
  • Prints the name and location.address of the destination 打印目标的名称和location.address
  • Updates the name and location.address of the destination 更新目标的名称和location.address
  • Saves changes to the ManagedObjectContext 保存对ManagedObjectContext的更改

When the app is run and re-run, only changes to the name will persist. 当应用程序运行并重新运行时,只会对名称进行更改。 Changes made to the location.address do not persist. 对location.address所做的更改不会保留。

import UIKit
import CoreData

class ViewController: UIViewController {

    var appDelegate: AppDelegate!
    var context: NSManagedObjectContext!

    override func viewDidLoad() {
        super.viewDidLoad()
        updateDestination()
    }

    func updateDestination() {
        var destination: Destination
        appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        context = appDelegate.managedObjectContext

        if let dest = fetchOneDestination() {
            destination = dest
        }
        else {
            destination = create()!
        }
        print("destination named: \(destination.name), at: \(destination.location.address)")

        destination.name = "New name of place that will update and persist"
        destination.location.address = "123 main st (change that will never persist)"
        appDelegate.saveContext()
    }

    func create() -> Destination? {
        guard let newDestination = NSEntityDescription.insertNewObjectForEntityForName("Destination", inManagedObjectContext: context) as? Destination else {
            return nil
        }
        newDestination.name = "Original name of place that can be updated"
        newDestination.location = Location(address: "100 main st", latitude: 34.051145, longitude: -118.243595)
        return newDestination
    }

    func fetchOneDestination() -> Destination? {
        let request = NSFetchRequest()
        request.entity = NSEntityDescription.entityForName("Destination", inManagedObjectContext: context)
        do {
            let fetchResults = try context.executeFetchRequest(request)
            if fetchResults.count > 0 {
                if let dest = fetchResults[0] as? Destination {
                    return dest
                }
            }
        }
        catch {}
        return nil
    }
}

How can I make updates to the destination's location attribute persist? 如何更新目标的位置属性?

Wain's answer is correct- it seems that the reference to the object needs to change in order for Core Data to persist the update. Wain的答案是正确的 - 似乎对对象的引用需要更改,以便Core Data保持更新。 Changing a child attribute on the Location instance will not update that reference. 更改Location实例上的子属性不会更新该引用。 Nor will it work to mutate and re-set the same object. 也不会改变和重新设置相同的对象。 Only when a new object reference is assigned will the change stick. 仅当分配了新的对象引用时,更改才会生效。

Here are some code examples: 以下是一些代码示例:

This does NOT work: 并不工作:

let newLocation = destination.location
newLocation.address = "a new street that doesn't stick"
destination.location = newLocation

But this does: 但这样做:

destination.location = Location(address: "a new street that sticks", latitude: 34.051145, longitude: -118.243595)

This also works, providing that the Location class implements the copyWithZone method: 这也有效,前提是Location类实现了copyWithZone方法:

let newLocation = destination.location.copy() as! Location
newLocation.address = "another new street that sticks"
destination.location = newLocation

core data can't track the dirty state of that object because it doesn't know about its internals. 核心数据无法跟踪该对象的脏状态,因为它不了解其内部。 Instead of mutating the object, create a copy, mutate that and then set the new object. 而不是改变对象,创建一个副本,改变它,然后设置新对象。 It may work to mutate and then re-set the same object, not sure, haven't tested it. 它可能会变异,然后重新设置相同的对象,不确定,没有测试它。

You can check, just mutate the address and then ask the managed object if it has changes, if not then it won't save. 您可以检查,只是改变地址,然后询问托管对象是否有更改,如果没有则则不会保存。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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