背景
CoreDataではRelationshipに対してDelete Ruleを設定しておくことができます。これにより、オブジェクトを削除した際に、関連するオブジェクトを連鎖的に削除することができます。(CascadeDeleteRule)
問題点
上記のDelete Ruleは非常に便利なのですが、この処理は常に遅延して実行されます。通常はこの遅延は問題になりませんが、オブジェクトの削除直後に集計等を行うと、削除されるはずのオブジェクトが含まれてしまう場合があります。
対策
NSManagedObjectが削除される際に呼び出されるprepareForDeletionというフックがあるので、これを利用して即時にオブジェクトを削除することが出来ます。下記のコードではモデル上で設定されたDelete Ruleに基づいて削除処理を行っています。
該当オブジェクトに実装
override func prepareForDeletion() {
super.prepareForDeletion()
btk_propagateDelete()
}
NSManagedObjectのエクステンション
import CoreData
extension NSManagedObject{
func btk_propagateDelete(){
if(managedObjectContext == nil){
return
}
let context = managedObjectContext!
let relationships = entity.relationshipsByName
let relationshipNames:[String] = {
var names:[String] = []
for name in relationships.keys{
names.append(name as! String)
}
return names
}()
for relationshipName in relationshipNames{
let desc = relationships[relationshipName] as! NSRelationshipDescription
if(desc.deleteRule == .CascadeDeleteRule){
if(desc.toMany){
if(desc.ordered){
let toMany = mutableOrderedSetValueForKey(relationshipName)
for obj in toMany.array as! [NSManagedObject]{
context.deleteObject(obj)
}
}
else{
let toMany = mutableSetValueForKey(relationshipName)
for obj in toMany.allObjects as! [NSManagedObject]{
context.deleteObject(obj)
}
}
}
else{
let obj = valueForKey(relationshipName) as! NSManagedObject
context.deleteObject(obj)
}
}
else if(desc.deleteRule == .NullifyDeleteRule){
if(desc.toMany){
if(desc.ordered){
let toMany = mutableOrderedSetValueForKey(relationshipName)
toMany.removeAllObjects()
}
else{
let toMany = mutableSetValueForKey(relationshipName)
toMany.removeAllObjects()
}
}
else{
setValue(nil, forKey: relationshipName)
}
}
}
}
}