Core Data应用开发实践指南
上QQ阅读APP看书,第一时间看更新

4.2 Delete规则

在配置关系的时候,一定要注意Delete Rule(Delete规则)。当我们删除某个对象时,该规则决定了与之相关的那些对象应该如何处理。可供选择的“Delete规则”有下面这几种:

·Nullify 大多数情况都可以采用这种默认的Delete规则。如果删除了某个对象,而该对象与其他对象的“关系”又受制于Nullify规则,那么这些对象就会把指向该对象的“关系”清空。比方说,有个名叫Kg的unit对象,它关联着一些item对象。假如items“关系”的Delete规则是Nullify,那么当把这个名为Kg的unit对象删掉之后,与它相关的那些item对象就会将其unit特性设为nil。

·Cascade 这种Delete规则会沿着关系来传播删除操作。比方说,有个名叫Kg的unit对象,它关联着一些item对象。假如items关系的Delete规则是Cascade,那么当把这个名为Kg的unit对象删掉之后,与它相关的所有item对象也会被删除。

·Deny 如果尚有其他对象与某对象相关联,那么这种Delete规则会阻止开发者删除该对象。比方说,有个名叫Kg的unit对象,它关联着一些item对象。假如items关系的Delete规则是Deny,那么当开发者把这个名为Kg的unit对象删除并试图将改动后的数据保存到上下文的时候,系统就会发现目前仍有item对象与之相关联,从而引发validation error(验证错误)。假如把某条关系的Delete规则设成了Deny,那么在删除源对象之前,开发者需要确保程序里面已经没有与该对象通过这条关系相关联的目标对象。

·No Action 这是一种奇怪的Delete规则,它会导致对象图处于不一致状态(inconsistent state)。假如运用了这条Delete规则,那么在删除某个对象之后,开发者必须手动设定反向的关系,以确保它们都指向有效的对象。只有在极个别的情况下才需要使用这种Delete规则。

为了测试删除对象之后的效果,我们需要添加一个新方法,用以显示持久化存储区里unit对象及item对象的个数。相关代码如程序清单4-2所示。

程序清单4-2 AppDelegate.m文件中的showUnitAndItemCount方法

请按下列步骤修改Grocery Dude,为测试Delete规则做准备:

1.把程序清单4-2中的showUnitAndItemCount方法添加到文件AppDelegate.m里,并放在现有的demo方法上方。

2.修改AppDelegate.m文件的demo方法,把原有的代码都删掉,只写一句[self showUnitAndItemCount];即可。

3.运行应用程序。控制台里应该会输出如图4-5所示的日志。

图4-5 持久化存储区里面item对象与unit对象的个数

根据图中显示的结果可知,持久化存储区里面有两个item对象和一个unit对象。这两个item对象是刚才在实现程序清单4-1的时候插入的,它们分别是oranges和bananas。仅有的那个unit对象就是Kg,而这两个item对象都与之相关联着。现在我们来看看如果Delete规则是Deny,那么在删掉名为Kg的unit对象时会发生什么。

请按下列步骤修改Grocery Dude,以便将Delete规则设为Deny:

1.在Model 5的Unit实体中选定items“关系”。

2.通过Data Model Inpector界面(可按“Option++3”组合键调出该界面),把items“关系”的Delete Rule设为Deny。

程序清单4-3中的这段代码用于删除名叫Kg的unit对象。

程序清单4-3 AppDelegate.m文件中的demo方法(用于删除unit对象)

请按下列步骤修改Grocery Dude,用代码来删掉名为Kg的unit对象:

1.修改AppDelegate.m文件中的demo方法,用程序清单4-3里的代码替换原有代码。

2.运行应用程序。你应该会在控制台中看到如图4-6所示的日志。

图4-6 将Delete规则设为Deny之后,它生效了吗?

根据控制台中的日志来看,这条Deny Delete规则好像并没有生效。这是怎么回事呢?按照刚才讲的,由于oranges和bananas这两个对象还与unit对象有关联,所以Deny规则应该阻止我们删除这个名为Kg的unit对象,但是现在为什么程序里面已经没有unit对象了呢?这些问题都问得很有道理,然而关键之处在于,只有当真正保存上下文的时候,系统才会去实施Delete规则。

请按下列步骤修改Grocery Dude,以便在删除unit对象之后保存上下文:

1.修改AppDelegate.m文件中demo的方法,在其底部添加[[self cdh]save-Context];语句。

2.重新运行应用程序,对上下文所做的save(保存)操作应该会失败,如图4-7所示。

图4-7 将Delete规则设为Deny之后,它会在保存上下文时生效

只有当尝试保存上下文的时候,系统才会去核查Delete规则是否生效。若发现违规,则会产生NSCocoaErrorDomain错误,它的错误码是1600。如果想解决这个错误,那就必须在删掉unit对象之前确保该对象可以安全地移除。

假如没办法安全地删除对象,那么可以采取以下两种办法:

·告知用户删除操作已遭系统拒绝,程序决定跳过该操作。

·先清空unit.items,然后再删掉unit对象。

在实际的应用程序中,当用户从表格视图界面里以滑动(swipe)的方式删除某个unit之后,可能就会引发这种错误。关系的Delete规则如果是Deny,那么开发者就应该使用超类NSManagedObject里面名为validateForDelete的方法来判断是否能够安全地移除该对象。假如该方法返回YES,那就表明可以把相关对象安全地删掉。程序清单4-4里的这段范例代码演示了此方法的用法。

程序清单4-4 AppDelegate.m文件中的demo方法(用于验证是否能够执行删除操作)

请按下列步骤修改Grocery Dude,以便在删除相关对象之前先验证删除操作是否符合Delete规则:

1.修改AppDelegate.m文件中的demo方法,用程序清单4-4把for循环体里原有的代码替换掉。

2.再度运行应用程序。这次应该会看到如图4-8所示的结果,也就是说,名为Kg的unit对象并未删掉。

图4-8 删除对象之前,先验证该操作是否符合Delete规则

下一步是把数据验证过程中所发生的错误展示给用户。尽管从用户体验的角度来讲这么做相当不好,但这也是迫不得已的办法。