为什么访问具有父关系的子类 NSManagedObject 的字符串参数会崩溃?

Why does accessing a String parameter of a subclassed NSManagedObject with a parent relationship crash?

我已经为两个核心数据实体生成了 classes。第一个称为地址,是一个抽象实体。第二个称为 Person,它继承自 Address。出于此测试的目的,我添加了一些示例托管属性。而且我已经向 Person class 添加了一个非托管字符串 属性。访问 Person class 的字符串 属性 会崩溃。为什么会崩溃?

Address 和 Person classes 由 Xcode 自动生成,除了额外的参数:let foo = "Foo"

如果我修改代码使 Person 直接从 NSManagedObject 继承而不是 Address,那么代码可以工作并且不会崩溃。

自动生成地址class:

@objc(Address)
public class Address: NSManagedObject {
}

extension Address {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Address> {
        return NSFetchRequest<Address>(entityName: "Address")
    }
    @NSManaged public var street: String?
    @NSManaged public var city: String?
}

自动生成的人 class,"foo" 参数除外:

@objc(Person)
public class Person: Address {
    public let foo = "Foo"  //added this parameter
}

extension Person {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
        return NSFetchRequest<Person>(entityName: "Person")
    }
    @NSManaged public var name: String?
}

问题代码

let person = Person(context: context)
print(person.foo) //doesn't crash, but prints empty line instead of value
print("VALUE:\(person.foo):") //crashes with Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)

更新: 如果 foo 定义为

public let foo: String? = "Foo"

然后打印语句不会崩溃,而是将值解释为 'nil' 并打印出来。

所以我的问题变成了:为什么这个作为常量赋值的值在幕后被重置为 nil?

我有两个挥手解释为什么你得到零:

  • 托管对象只有在插入后才能很好地发挥作用。
  • 您的 foo 是一个我称之为 存储的常量 属性。我编造了这个名字,因为红旗,我在 Swift book chapter on Properties
  • 中找不到任何例子

将这两个放在一起,你会得到一个不起作用的边缘情况。

话虽这么说,我有点惊讶您的 foo 设置不起作用,因为 foo 不是托管的 属性(也就是说,它不在数据模型)。如果我将这样的 常量存储 属性 在常规的非托管对象中……

public class Animal {
    public let foo: String! = "Foo"
}

它稍后会按预期读取。

因此,如果您可以接受这种极端情况 在 Core Data 中行不通 ,您可以继续使用几种更正常的工作方式。

一种方法是将 foo 声明为 var 并在 awakeFromInsert() 中赋值,正如我之前提到的,在插入之后。在 Core Data 中,awakeFromInsert() 是你的一位朋友…

@objc(Person)
public class Person: Address {
    public var foo: String!
    override public func awakeFromInsert() {
        foo = "Foo"
    }
}

另一种可行的方法是计算 属性…

@objc(Person)
public class Person: Address {
    public var foo : String { return "Foo" }
}

最后,最合乎逻辑的方法是将其设为 类型 属性

,因为 foo 对于所有实例都是常量
@objc(Person)
public class Person: Address {
    static var foo: String = "Foo"
}

当然,如果您这样做,您必须将其引用为 Person.foo 而不是 person.foo