可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
In CoreData, I have defined an unordered to-many relationship from Node
to Tag
. I've created an Swift entity like this:
import CoreData class Node : NSManagedObject { @NSManaged var tags : Array }
Now I want to add a Tag
to an instance of Node
, like this:
var node = NSEntityDescription.insertNewObjectForEntityForName("Node", inManagedObjectContext: managedObjectContext) as Node node.tags.append(tag)
However, this fails with the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-many relationship: property = "tags"; desired type = NSSet; given type = _TtCSs22ContiguousArrayStorage000000000B3440D4; value = ( "<_ttc8motornav3tag:> (entity: Tag; id: 0xb343800 ; data: {...})" ).'
What is the correct type for to-many relationships?
回答1:
To be able to work with one-to-many relationship in Swift you need to define property as:
class Node: NSManagedObject { @NSManaged var tags: NSSet }
If you try to use NSMutableSet
changes will not be saved in CoreData. And of course it is recommended to define reverse link in Node
:
class Tag: NSManagedObject { @NSManaged var node: Node }
But still Swift cannot generate dynamic accessors in runtime, so we need to define them manually. It is very convenient to define them in class extension
and put in Entity+CoreData.swift
file. Bellow is content of Node+CoreData.swift
file:
extension Node { func addTagObject(value:Tag) { var items = self.mutableSetValueForKey("tags"); items.addObject(value) } func removeTagObject(value:Tag) { var items = self.mutableSetValueForKey("tags"); items.removeObject(value) } }
Usage:
// somewhere before created/fetched node and tag entities node.addTagObject(tag)
Important: To make it all work you should verify that class names of entities in you CoreData model includes your module name. E.g. MyProjectName.Node
回答2:
As of Xcode 7 and Swift 2.0, the release note 17583057 states:
The NSManaged attribute can be used with methods as well as properties, for access to Core Data’s automatically generated Key-Value-Coding-compliant to-many accessors.
@NSManaged var employees: NSSet @NSManaged func addEmployeesObject(employee: Employee) @NSManaged func removeEmployeesObject(employee: Employee) @NSManaged func addEmployees(employees: NSSet) @NSManaged func removeEmployees(employees: NSSet)
These can be declared in your NSManagedObject subclass. (17583057)
So you just have to declare the following methods and CoreData will take care of the rest:
@NSManaged func addTagsObject(tag: Tag) @NSManaged func removeTagsObject(tag: Tag) @NSManaged func addTags(tags: NSSet) @NSManaged func removeTags(tags: NSSet)
回答3:
Actually you can just define:
@NSManaged var employees: Set
And use the insert
and remove
methods of the Set
directly.
回答4:
Building on @Keenle's answer, if you want to be cheeky and concise and be able to say
node.tags.append(tag)
one can wrap the call to self.mutableSetValueForKey:
class Node: NSManagedObject { var tags: NSMutableOrderedSet { return self.mutableOrderedSetValueForKey("tags") } }