【问题标题】:Why does accessing a String parameter of a subclassed NSManagedObject with a parent relationship crash?为什么访问具有父关系的子类 NSManagedObject 的 String 参数会崩溃?
【发布时间】:2019-08-21 10:52:24
【问题描述】:

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

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

如果我修改代码以使 Person 直接从 NSManagedObject 继承而不是 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"

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

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

【问题讨论】:

    标签: swift core-data crash macos-mojave swift5


    【解决方案1】:

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

    • 托管对象在插入之前无法正常工作。
    • 您的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 对于所有实例都是常量,因此将其设为 type 属性...

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

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

    【讨论】:

    • 谢谢,这很有帮助。我已经尝试了您建议的方法,它们都有效。所以现在我可以选择可以使用的解决方法。出于好奇,我将 foo 上的类型更改为 Int 并将其赋值为 1。我看到的值作为 String 类型的行为略有变化。在我遇到崩溃问题的地方访问 foo 时,我可以看到 foo 被初始化为 0 而不是 1。正如您所建议的,在 awakeFromInsert() 中设置值可以解决问题。使值成为计算属性也是如此。
    猜你喜欢
    • 2010-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-17
    相关资源
    最近更新 更多