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

Dan*_*n G 6 core-data ios swift

我已经构建了一个非常基本的示例来演示我尝试更新可转换类型并在应用程序重新启动之间保持更改的问题.

我有一个Destination类型的实体...

import Foundation
import CoreData

class Destination: NSManagedObject {
    @NSManaged var name: String
    @NSManaged var location: Location
}
Run Code Online (Sandbox Code Playgroud)

...具有简单的名称属性(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")
    }
}
Run Code Online (Sandbox Code Playgroud)

位置在Core Data中配置为"可转换",因为它具有其他基本类型都无法处理的结构.

使用Apple的样板核心数据代码,这里是一个视图控制器,它只执行以下操作:

  • 获取必要的appDelegate/ManagedApplicationContext引用
  • 如果存在目的地,则获取目的地,否则创建一个目的地
  • 打印目标的名称和location.address
  • 更新目标的名称和location.address
  • 保存对ManagedObjectContext的更改

当应用程序运行并重新运行时,只会对名称进行更改.对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
    }
}
Run Code Online (Sandbox Code Playgroud)

如何更新目标的位置属性?

Dan*_*n G 5

Wain 的回答是正确的 - 似乎需要更改对对象的引用,以便 Core Data 保持更新。更改 Location 实例上的子属性不会更新该引用。它也不会改变和重新设置同一个对象。只有当分配了新的对象引用时,更改才会生效。

下面是一些代码示例:

但这工作:

let newLocation = destination.location
newLocation.address = "a new street that doesn't stick"
destination.location = newLocation
Run Code Online (Sandbox Code Playgroud)

但这确实:

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

这也有效,前提是 Location 类实现了 copyWithZone 方法:

let newLocation = destination.location.copy() as! Location
newLocation.address = "another new street that sticks"
destination.location = newLocation
Run Code Online (Sandbox Code Playgroud)


Wai*_*ain 4

核心数据无法跟踪该对象的脏状态,因为它不知道其内部结构。不要改变对象,而是创建一个副本,改变它,然后设置新对象。变异然后重新设置相同的对象可能会起作用,不确定,还没有测试过。

您可以检查,只需改变地址,然后询问托管对象是否有变化,如果没有,则不会保存。