I found it recently in my program, the duplications are not stopped, even I used the Set. The object is called Category, with two variables.
import Foundation
import CoreData
extension Category {
@NSManaged var name: String?
@NSManaged var items: NSSet?
}
class Category: NSManagedObject {
override var hashValue: Int {
return name!.hashValue
}
}
func == (left: Category, right: Category) -> Bool {
return left.name == right.name
}
I have override the hashValue: Int and the == method in the class, but the Set still consider them as two objects. Does that mean Set doesn't work for NSManagedObjects or I have something left need to be done?
Here is my unit test code:
guard let cate1 = NSEntityDescription.insertNewObjectForEntityForName("Category", inManagedObjectContext: context) as? MyProgram.Category else { return }
cate1.name = "Cate"
cate1.items = nil
guard let cate2 = NSEntityDescription.insertNewObjectForEntityForName("Category", inManagedObjectContext: context) as? MyProgram.Category else { return }
cate2.name = "Cate"
cate2.items = nil
let combine = Set<MyProgram.Category>([cate1, cate2])
assert(cate1.hashValue == cate2.hashValue)
assert(combine.count == 1)
It will fail at the last line of code. Can anybody who knows the reason gives me some advices?
Unfortunately, what you are trying to do is not possible with Core Data managed objects.
For subclasses of NSObject
, you would have to override isEqual:
and hash
(instead of ==
and hashValue
), see NSObject subclass in Swift: hash vs hashValue, isEqual vs ==.
For NSManagedObject
subclasses, it is explicitly "forbidden" to
override isEqual:
and hash
, see NSManagedObject Class Reference. The reason is
that you can compare managed objects for equality without retrieving all their
properties from the persistent store ("fire a fault").
As a consequence you cannot define your own notion of "equality"
for Core Data managed objects in a way that it works with Set
or
NSSet
.
To check if a set already contains a managed object with a given property you can do something like
if (combine.contains { $0.name == cate2.name }) {
}
In order to avoid Category objects with the same name, you would have to execute a fetch request which checks if an object with the given name already exists.