您在应用程序中所做的可能是使用核心数据最常见的失败。绝不应该鼓励您像以前那样直接使用核心数据类,始终使用子类化、类别或(最好的)使用包装器。
因为在大多数情况下,当您在模型中编辑实体时,您希望删除自动生成的文件并创建新文件,这些文件不应被修改。这是我不鼓励您使用任何快速修复(例如修改模型中的名称然后创建自定义设置器)的主要原因。
为什么我最推荐包装器是因为您可以使用它构建自己的界面。您可以根据需要在其上创建任意数量的方法、附件,可以使用数据保护,例如具有只读参数...因此,当您修改数据模型时,应用程序应该完全没有区别,当您可能会为一些优化或一些内部功能添加一些额外的表格,你不会有隐藏这些附件的问题。除此之外,拥有一个额外的层将使您可以非常轻松地创建一些缓存,轻松调试,因为您可以在或多或少的任何附件中放置断点或日志,您可以在内部维护多线程操作......
我现在可以理解,将您的代码迁移到其他系统可能需要一些时间,但这是您应该考虑的事情。如果应用程序尚未完成,我建议您做迁移它:如果您创建一个具有与应用程序中已使用的相同属性的包装器,则可以简单地更改它已经存在的类名使用,这应该不会花费太长时间。如果您选择继续按原样工作,您很可能会遇到一些更难的问题,如果没有其他问题,请记住这一点,当您开始一个新的应用程序时。
编辑:包装器说明和示例
包装器是指一个类实例,它包含另一个实例并围绕它构建一个接口。让我先给你看一个很好的例子:
界面:
@interface EntityWrapper : NSObject
@property NSInteger number;
@property UIImage *image;
+ (NSArray *)fetchAll;
+ (void)invalidateCache;
@end
实施:
@class EntityName;
static NSArray *__entityCache = nil;
@interface EntityWrapper() {
EntityName *_boundEntity;
}
@end
@implementation EntityWrapper
- (instancetype)initWithEntity:(EntityName *)entity {
if((self = [super init])) {
_boundEntity = entity;
}
return self;
}
+ (NSArray *)fetchAll {
if(__entityCache == nil) {
NSMutableArray *toReturn = [[NSMutableArray alloc] init];
NSArray *entityArray = nil; //fetch from data base
for(EntityName *entity in entityArray)
[toReturn addObject:[[EntityWrapper alloc] initWithEntity:entity]];
__entityCache = [toReturn copy];
}
return __entityCache;
}
+ (void)invalidateCache {
__entityCache = nil;
}
- (void)setNumber:(NSInteger)number {
_boundEntity.number = @(number);
}
- (NSInteger)number {
return [_boundEntity.number integerValue];
}
- (void)setImage:(UIImage *)image {
_boundEntity.image = UIImagePNGRepresentation(image);
}
- (UIImage *)image {
return [[UIImage alloc] initWithData:_boundEntity.image];
}
@end
正如您在此处看到的,我正在使用自定义 setter 和 getter 围绕实体构建一个界面。我什至创建了一个从数据库中获取所有对象的方法,下一步可能是使用一些谓词或者在这种情况下使用一些自定义选项来获取它们。我还添加了一个最简单的缓存,只是为了了解这个概念。
number 现在是NSInteger 而不是NSNumber,这可能非常方便,但要小心这样做,因为您可能需要知道number 是否为nil。对于这种情况,您还可以创建另一个属性,例如 BOOL numberIsSet。
图片也差不多。您根本不需要转换器,只需要一个 getter 和一个 setter(这与转换器几乎相同,但这种方法更具动态性)。
因此,创建包装器可以为您提供终极力量。我们的想法是创建尽可能小且尽可能简单的界面。这意味着头文件中的方法与需要的方法一样多,其余部分是隐藏的。这些方法背后的逻辑可能非常复杂,但仍然可以维护,因为它是一个封闭系统(不依赖于实体,只依赖于实体),举个例子:
界面:
@property (readonly) NSDecimalNumber *heavyValue;
实施:
- (NSDecimalNumber *)heavyValue {
NSDecimalNumber *valueA = _boundEntity.valueA;
NSDecimalNumber *valueB = _boundEntity.valueB;
NSDecimalNumber *valueC = _boundEntity.valueC;
return [[valueA decimalNumberByAdding:valueB] decimalNumberByDividingBy:valueC];
}
现在这是一个相当标准的过程,并且效果很好,但是这种方法在 CPU 上可能非常繁重。如果此方法被多次调用,您可能会通过将结果存储到实体本身来进行优化。所以你要做的就是在模型heavyValue和代码中添加另一个值:
- (NSDecimalNumber *)heavyValue {
NSDecimalNumber *toReturn = _boundEntity.heavyValue;
if(toReturn == nil) {
NSDecimalNumber *valueA = _boundEntity.valueA;
NSDecimalNumber *valueB = _boundEntity.valueB;
NSDecimalNumber *valueC = _boundEntity.valueC;
toReturn = [[valueA decimalNumberByAdding:valueB] decimalNumberByDividingBy:valueC];
_boundEntity.heavyValue = toReturn;
}
return toReturn;
}
- (void)setValueA:(NSDecimalNumber *)valueA {
_boundEntity.valueA = valueA;
_boundEntity.heavyValue = nil; //this invalidates the value
}
因此,在一个简单的 getter 背后,这是一个相当大的逻辑变化,但您的其余代码没有受到伤害,它仍然可以正常工作。