Reputation: 33
I have generated classes for two core data entities. The first is called Address and is an abstract entity. The second is called Person, and it inherits from Address. I've added a few example managed attributes for the purpose of this test. And i've added a non-managed String property to the Person class. Accessing the string property of the Person class will crash. Why does this crash?
The Address and Person classes are automatically generated by Xcode, with the exception of the extra parameter: let foo = "Foo"
If i modify the code to make Person inherit from NSManagedObject directly instead of Address, then the code works and doesn't crash.
Automatically generated 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?
}
Automatically generated person class with the exception of the "foo" parameter:
@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?
}
problem code
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)
UPDATE: if foo is defined as
public let foo: String? = "Foo"
then the print statements don't crash, instead they interpret the value as 'nil' and print that.
So my question becomes: Why is this value which is assigned as a constant being reset to nil under the covers?
Upvotes: 3
Views: 103
Reputation: 5030
I have two hand-waving explanations why you are getting nil:
foo
is a what I would call a constant stored property. I made up the name because, red flag, I cannot find any examples of it in the Swift book chapter on PropertiesPut these two together and you get an edge case that doesn't work.
That being said, I'm kind of surprised that your foo
setting does not work, because foo
is not a managed property (that is, it is not in the data model). If I make such a constant stored property in a regular, non-managed object…
public class Animal {
public let foo: String! = "Foo"
}
it reads back later as expected.
So, if you can accept that this edge case just doesn't work in Core Data, you can move on to several more normal ways that do work.
One way is to declare foo
as a var
and assign a value to in awakeFromInsert()
which is, as I alluded to earlier, after insertion. In Core Data, awakeFromInsert()
is one of your friends…
@objc(Person)
public class Person: Address {
public var foo: String!
override public func awakeFromInsert() {
foo = "Foo"
}
}
Another way that works is as a computed property…
@objc(Person)
public class Person: Address {
public var foo : String { return "Foo" }
}
And, finally, the most logical way, since foo is constant for all instances, is to make it a type property…
@objc(Person)
public class Person: Address {
static var foo: String = "Foo"
}
but of course if you do this you must reference it as Person.foo
instead of person.foo
.
Upvotes: 0