为什么在父关系崩溃的情况下访问子类NSManagedObject的String参数?

问题描述 投票:3回答:1

我已经为两个核心数据实体生成了类。第一个叫做Address,是一个抽象的实体。第二个叫做Person,它继承自Address。我为此测试添加了一些示例托管属性。我已经为Person类添加了一个非托管的String属性。访问Person类的字符串属性将崩溃。为什么会崩溃?

Address和Person类由Xcode自动生成,但额外参数除外:let foo =“Foo”

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

自动生成的Address类:

@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?
}

自动生成的person类,但“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"

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

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

swift core-data crash macos-mojave swift5
1个回答
0
投票

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

  • 在插入之前,托管对象的功能不会很好。
  • 你的foo是我所谓的恒定存储属性。我编造了这个名字,因为,红旗,我在Swift book chapter on Properties找不到它的任何例子

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

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

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

它按预期稍后回读。

因此,如果您可以接受此边缘情况在Core Data中不起作用,您可以继续使用其他几种正常工作方式。

一种方法是将foo声明为var,并在awakeFromInsert()中指定一个值,正如我之前提到的那样,在插入之后。在核心数据中,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引用。

© www.soinside.com 2019 - 2024. All rights reserved.