Лучший способ реализовать перечисления с помощью Core Data
-
06-07-2019 - |
Вопрос
Каков наилучший способ привязать объекты 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. Я сделал это для очень сложных моделей, и это помогло мне сэкономить много времени. Р>