Learning to be Giant.

扩展NSManagedObject的子类的方法

|

开发当中我们常常会遇到需要扩展由Xcode生成的NSManagedObject的子类的情况,因为由Data Model生成的Entity Class每一次在Model重新生成的时候都会被覆盖,这使得如果想要给这些Entity Class扩展代码的话,没有办法直接在原有的文件当中处理,需要找一个办法解决这个问题。

可选方案

  1. Category:通过Category来扩展子类。使用Category是常用的实践,网上大都推荐这个方法

    优点:使用方便,编写方便
    缺点:不支持新加property支持声明property,但是不能合成变量,但是这个就足够满足我们的需求了

  2. Subclassing:继承生成的Entity Class,我倾向于这个方法

    优点:使用方便,编写方便,可以添加property
    缺点:后面讲

  3. Wrapper:将Entity Class的实例包裹起来

    优点:编写方便,可以添加property
    缺点:使用原有property需要好几个点,如果只希望一个点需要写很多的mapping

  4. Version Control:利用版本控制merge重新生成代码之前的代码,恢复之前在子类当中写的代码

    优点:解决了代码覆盖的问题,也比较方便
    缺点:会有一些merge commit,但不是大问题

Category vs. Subclassing

在大多数情况下Category能够完美胜任我们的要求,比如category可以提供不需要实例化的property,可以扩展一些我们需要的方法等等。但是很多时候我们需要在Entity当中通过override validateValue:forKey:的方法写一些validation的代码,这个时候使用category就不是很恰当了。苹果的文档是这么说的:

If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes.

在我们的子类的实现当中不仅扩展了函数,同时也override了父类的validation,如果采用category的话相当于我们需要覆盖原来的validation,根据:http://stackoverflow.com/questions/5272451/overriding-methods-using-categories-in-objective-c,这似乎不是一个好的思路。

怎么做

基本思路

在Data Model编辑器的Configuration当中把所有的Entity对应的Class修改成DBEntity,其中Entity是Entity的名称,然后用Xcode生成代码。这样生成的代码的类名和文件名就都是DBEntity了。这时候我们创建一系列的Entity的类来继承DBEntity,并在这些子类当中写我们自定义的代码。然后我们需要将Configuration里面的Entity名称由DBEntity再改回原来的Entity,否则的话在运行时生成Entity的时候会生成DBEntity实例,我们的自定义就没有意义了。

什么意思呢?比如我们的项目,生成的Entity Class是DBWordStatus,但是我们使用的却是WordStatus。当我们向数据库中添加数据的时候:

WordStatus * status = [WordStatus MR_createEntity]; // MagicalRecord
WordStatus * status = [NSEntityDescription insertNewObjectForEntityForName:@"WordStatus" inManagedObjectContext:context]; // Core Data

我们以为我们拿到的status指向的是一个WordStatus对象,而其实指向的是一个DBWordStatus对象,也就是说,一个子类指针指向了父类对象,理论上是不合法的,但是由于这个是运行时出现的问题,所以编译不会报错,一般运行时也不会报错。可是,当我们尝试在WordStatus定义了一些新的方法,并且尝试调用的时候,就会抛出异常,提示DBWordStatus当中并不存在对应的方法。

另外还需要注意的是,下一次修改了Data Model重新生成之前,也需要再将Configuration改回DBEntity去,否则会覆盖掉Entity的文件。还有,这样生成的Relation的类型依然是DBEntity的类型,我们需要手动将DBEntity改成Entity,否则用起来会很不方便。

这整个过程看起来比较复杂,但是我们可以写一些脚本来帮助我们处理文件的修改。

工具

事实上虽然这么复杂,我们却可以使用mogenerator来完成上面说的这一大堆。使用方法非常简单,网上一搜就有。唯一的缺点就是如果我们使用MagicalRecord的话,这个工具生成的很多代码对我们就没有意义了,不过好在我们可以修改它的template来删除这些代码。

Disclaimer: This is a personal weblog. The opinions expressed here represent my own and not those of any entity with which I have been, am now, or will be affiliated.

Comments