Лучший способ реализовать перечисления с помощью Core Data

StackOverflow https://stackoverflow.com/questions/1624297

Вопрос

Каков наилучший способ привязать объекты Core Data к значениям enum, чтобы я мог присвоить объекту свойство type?Другими словами, у меня есть объект под названием Item с помощью itemType свойство, которое я хочу привязать к перечислению, каков наилучший способ сделать это.

Это было полезно?

Решение

Вам придется создавать собственные средства доступа, если вы хотите ограничить значения перечислением. Итак, сначала вы должны объявить перечисление, вот так:

typedef enum {
    kPaymentFrequencyOneOff = 0,
    kPaymentFrequencyYearly = 1,
    kPaymentFrequencyMonthly = 2,
    kPaymentFrequencyWeekly = 3
} PaymentFrequency;

Затем объявите методы получения и установки для вашей собственности. Это плохая идея переопределять существующие, поскольку стандартные средства доступа ожидают объект NSNumber, а не скалярный тип, и вы столкнетесь с проблемами, если что-нибудь в привязках или системах KVO попытается получить доступ к вашему значению.

- (PaymentFrequency)itemTypeRaw {
    return (PaymentFrequency)[[self itemType] intValue];
}

- (void)setItemTypeRaw:(PaymentFrequency)type {
    [self setItemType:[NSNumber numberWithInt:type]];
}

Наконец, вы должны реализовать + keyPathsForValuesAffecting<Key>, чтобы получать уведомления KVO для itemTypeRaw при изменении itemType.

+ (NSSet *)keyPathsForValuesAffectingItemTypeRaw {
    return [NSSet setWithObject:@"itemType"];
}

Другие советы

Вы можете сделать это намного проще:

typedef enum Types_e : int16_t {
    TypeA = 0,
    TypeB = 1,
} Types_t;

@property (nonatomic) Types_t itemType;

А в вашей модели установите itemType как 16-битное число. Все сделано. Дополнительный код не требуется. Просто вставьте в свой обычный

@dynamic itemType;

Если вы используете XCode для создания своего подкласса NSManagedObject, убедитесь, что & использует скалярные свойства для примитивных типов данных " настройка проверена.

Альтернативный подход, который я рассматриваю, - это вовсе не объявлять перечисление, а вместо этого объявлять значения как методы категорий в NSNumber.

Если вы используете mogenerator, взгляните на это: https : //github.com/rentzsch/mogenerator/wiki/Using-enums-as-types . У вас может быть атрибут Integer 16 с именем itemType, со значением attributeValueScalarType Item в пользовательской информации. Затем в пользовательской информации для вашей сущности установите additionalHeaderFileName имя заголовка, в котором определено перечисление <=>. При создании файлов заголовков mogenerator автоматически придает свойству тип <=>.

Я устанавливаю тип атрибута как 16-битное целое число, затем использую это:

#import <CoreData/CoreData.h>

enum {
    LDDirtyTypeRecord = 0,
    LDDirtyTypeAttachment
};
typedef int16_t LDDirtyType;

enum {
    LDDirtyActionInsert = 0,
    LDDirtyActionDelete
};
typedef int16_t LDDirtyAction;


@interface LDDirty : NSManagedObject

@property (nonatomic, strong) NSString* identifier;
@property (nonatomic) LDDirtyType type;
@property (nonatomic) LDDirtyAction action;

@end

...

#import "LDDirty.h"

@implementation LDDirty

@dynamic identifier;
@dynamic type;
@dynamic action;

@end

Поскольку перечисления поддерживаются стандартным сокращением, вы также не могли бы использовать оболочку NSNumber и устанавливать свойство непосредственно как скалярное значение.Убедитесь, что тип данных в базовой модели данных задан как "Целое число 32".

Моя личность.h

typedef enum {
kEnumThing, /* 0 is implied */
kEnumWidget, /* 1 is implied */
} MyThingAMaBobs;

@interface myEntity : NSManagedObject

@property (nonatomic) int32_t coreDataEnumStorage;

В другом месте кода

myEntityInstance.coreDataEnumStorage = kEnumThing;

Или синтаксический анализ из строки JSON, или загрузка из файла

myEntityInstance.coreDataEnumStorage = [myStringOfAnInteger intValue];

Приведенный ниже код работает для меня, и я добавил его в качестве полного рабочего примера.Я хотел бы услышать мнения об этом подходе, поскольку я планирую широко использовать его во всех своих приложениях.

  • Я оставил @dynamic на месте, так как затем он удовлетворяется получателем / установщиком, указанным в свойстве.

  • Согласно ответу iKenndac, я не переопределял имена получателей / установщиков по умолчанию.

  • Я включил некоторую проверку диапазона с помощью NSAssert для допустимых значений typedef.

  • Я также добавил метод для получения строкового значения для данного typedef.

  • Я ставлю перед константами префикс "c", а не "k".Я знаю причину, стоящую за буквой "k" (математическое происхождение, историческое), но мне кажется, что я читаю ESL-код с ее помощью, поэтому я использую "c".Просто личное дело.

Здесь возникает аналогичный вопрос: typedef как основной тип данных

Я был бы признателен за любой вклад в этот подход.

Word.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

typedef enum {
    cPresent            = 0,    
    cFuturProche        = 1,    
    cPasseCompose       = 2,    
    cImparfait          = 3,    
    cFuturSimple        = 4,    
    cImperatif          = 5     
} TenseTypeEnum;

@class Word;
@interface Word : NSManagedObject

@property (nonatomic, retain) NSString * word;
@property (nonatomic, getter = tenseRaw, setter = setTenseRaw:) TenseTypeEnum tense;

// custom getter & setter methods
-(void)setTenseRaw:(TenseTypeEnum)newValue;
-(TenseTypeEnum)tenseRaw;
- (NSString *)textForTenseType:(TenseTypeEnum)tenseType;

@end


Word.m


#import "Word.h"

@implementation Word

@dynamic word;
@dynamic tense;

// custom getter & setter methods
-(void)setTenseRaw:(TenseTypeEnum)newValue
{
    NSNumber *numberValue = [NSNumber numberWithInt:newValue];
    [self willChangeValueForKey:@"tense"];
    [self setPrimitiveValue:numberValue forKey:@"tense"];
    [self didChangeValueForKey:@"tense"];
}


-(TenseTypeEnum)tenseRaw
{
    [self willAccessValueForKey:@"tense"];
    NSNumber *numberValue = [self primitiveValueForKey:@"tense"];
    [self didAccessValueForKey:@"tense"];
    int intValue = [numberValue intValue];

    NSAssert(intValue >= 0 && intValue <= 5, @"unsupported tense type");
    return (TenseTypeEnum) intValue;
}


- (NSString *)textForTenseType:(TenseTypeEnum)tenseType
{
    NSString *tenseText = [[NSString alloc] init];

    switch(tenseType){
        case cPresent:
            tenseText = @"présent";
            break;
        case cFuturProche:
            tenseText = @"futur proche";
            break;
        case cPasseCompose:
            tenseText = @"passé composé";
            break;
        case cImparfait:
            tenseText = @"imparfait";
            break;
        case cFuturSimple:
            tenseText = @"futur simple";
            break;
        case cImperatif:
            tenseText = @"impératif";
            break;
    }
    return tenseText;
}


@end

Я много сделал и считаю следующую форму полезной:

// accountType
public var account:AccountType {
    get {
        willAccessValueForKey(Field.Account.rawValue)
        defer { didAccessValueForKey(Field.Account.rawValue) }
        return primitiveAccountType.flatMap { AccountType(rawValue: $0) } ?? .New }
    set {
        willChangeValueForKey(Field.Account.rawValue)
        defer { didChangeValueForKey(Field.Account.rawValue) }
        primitiveAccountType = newValue.rawValue }}
@NSManaged private var primitiveAccountType: String?

В этом случае перечисление довольно простое:

public enum AccountType: String {
    case New = "new"
    case Registered = "full"
}

и называю это педантичным, но я использую перечисления для имен полей, например:

public enum Field:String {

    case Account = "account"
}

Поскольку это может стать трудоемким для сложных моделей данных, я написал генератор кода, который использует MOM / сущности, чтобы выплюнуть все сопоставления. Мои входные данные в конечном итоге представляют собой словарь из таблицы / строки в тип Enum Пока я занимался этим, я также генерировал код сериализации JSON. Я сделал это для очень сложных моделей, и это помогло мне сэкономить много времени.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top