Setter,Getters和Exposing真正的核心数据类型,我该怎么做?

时间:2014-07-20 19:09:57

标签: ios cocoa-touch cocoa core-data

我有一个核心数据实体,其结构如下:

number (integer 16),
reference (binary)
image (binary)

我为该实体创建了一个类。

该类的标题具有实体属性的这些声明。

@property (nonatomic, retain) NSNumber * number;
@property (nonatomic, retain) NSData * reference;
@property (nonatomic, retain) NSData * image;

但事实上,这3个属性是

number = NSInteger
reference = NSArray
image = UIImage

因为我无法直接在核心数据上存储数组和图像,所以我必须将其转换为NSData才能保存,但我并不关心被声明为NSData的属性,因为NSData的转换是内部的到实体,不应该暴露在课外的代码。

我希望将这些标头属性声明为

@property (nonatomic, assign) NSInteger number;
@property (nonatomic, retain) NSArray * reference;
@property (nonatomic, retain) UIImage * image;

我希望,例如,当我将一个数组分配给reference并在内部转换为NSData时。

我知道我必须创建setter和getter来做到这一点但我的问题是实体已经在使用这些名称。显然,我可以重命名所有核心数据实体,使其具有xnumber,xreference,ximage等前缀,因此我想要公开的名称/类型与我想在内部隐藏的名称/类型之间不会发生冲突。

还有其他替代方案吗?我的恐惧以同样的参考海洋而告终。

1 个答案:

答案 0 :(得分:1)

您在应用程序中所做的事情可能是使用核心数据时最常见的失败。绝不应该像你一样直接使用核心数据类,总是使用子类,类别或(最重要的)使用包装器。

由于在大多数情况下,当您在模型中编辑实体时,您希望删除自动生成的文件并创建新文件,这些文件应该是未修改的。这是我不鼓励您使用任何快速修复的主要原因,例如修改模型中的名称,然后创建自定义设置器。

为什么我建议包装器最多是因为你可以用它构建自己的界面。您可以根据需要创建尽可能多的方法,附件,您可以使用数据保护,例如具有只读参数......因此,当您修改数据模型时,应用程序应该没有区别,当您可能会添加一些额外的表进行一些优化或一些内部功能,你可以没有隐藏这些配件的问题。除此之外,有一个额外的层将使你很容易创建一些缓存,轻松调试,因为你可以放置一个断点或记录或多或少的任何和所有附件,你可以在内部维护多线程操作......

我可以理解,此时将代码迁移到其他系统可能需要一段时间,但这是应该考虑的事情。如果应用程序几乎已完成,我建议您执行迁移它:如果您创建的包装器具有与应用程序中已使用的属性相同的属性,则可以简单地更改已经存在的类名称使用,这不应该花太长时间。如果您选择继续工作,那么您很可能会遇到一些更难的问题,如果您开始新的应用程序时没有其他任何记忆。

编辑:包装说明和示例

通过包装器,我指的是一个包含另一个实例并在其周围构建接口的类实例。我先告诉你一个很好的例子:

<强>接口

@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是否{ {1}}。对于这种情况,您还可以创建另一个属性,例如nil

图像几乎一样。你根本不需要变换器,只需要一个吸气剂和一个定位器(这与变压器几乎相同,但这种方法更加动态)。

因此,创建包装类可以为您提供终极力量。我们的想法是尽可能简单地创建尽可能小的界面。这意味着头文件中的方法与所需的一样多,其余是隐藏的。这些方法背后的逻辑可能非常复杂,但仍然可以维护,因为它是一个封闭的系统(不依赖于实体),为您举个例子:

<强>接口

BOOL numberIsSet

<强>实施

@property (readonly) NSDecimalNumber *heavyValue;

现在这是一个非常标准的程序,并且可以很好地工作,但是这种方法对CPU来说非常重要。如果批量调用此方法,您可能会通过将结果存储到实体本身来达到要优化的程度。所以你要做的就是在模型- (NSDecimalNumber *)heavyValue { NSDecimalNumber *valueA = _boundEntity.valueA; NSDecimalNumber *valueB = _boundEntity.valueB; NSDecimalNumber *valueC = _boundEntity.valueC; return [[valueA decimalNumberByAdding:valueB] decimalNumberByDividingBy:valueC]; } 和代码中添加另一个值:

heavyValue

因此,简单的getter背后的逻辑是一个非常极端的变化,但是你的代码的其余部分没有受到伤害,它仍然可以正常工作。