Pregunta

¿Cuál es la mejor manera de vincular las entidades de Core Data con valores de enumeración para que yo pueda asignar una propiedad de tipo a la entidad? En otras palabras, tengo una entidad llamada Item con una propiedad itemType que quiero estar vinculada a una enumeración, ¿cuál es la mejor manera de hacerlo?

¿Fue útil?

Solución

Tendrá que crear accesores personalizados si desea restringir los valores a una enumeración. Entonces, primero declararías una enumeración, así:

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

Luego, declare getters y setters para su propiedad. Es una mala idea anular los existentes, ya que los accesos estándar esperan un objeto NSNumber en lugar de un tipo escalar, y se encontrará con problemas si algo en los enlaces o sistemas KVO intenta acceder a su valor.

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

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

Finalmente, debe implementar + keyPathsForValuesAffecting<Key> para que reciba notificaciones KVO para itemTypeRaw cuando cambie itemType.

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

Otros consejos

Puede hacerlo de esta manera, más simple:

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

@property (nonatomic) Types_t itemType;

Y en su modelo, configure itemType como un número de 16 bits. Todo listo. No se necesita código adicional. Solo pon tu habitual

@dynamic itemType;

Si está utilizando Xcode para crear su subclase NSManagedObject, asegúrese de que " use propiedades escalares para tipos de datos primitivos " la configuración está marcada.

Un enfoque alternativo que estoy considerando es no declarar una enumeración en absoluto, sino declarar los valores como métodos de categoría en NSNumber.

Si está utilizando mogenerator, eche un vistazo a esto: https : //github.com/rentzsch/mogenerator/wiki/Using-enums-as-types . Puede tener un atributo Integer 16 llamado itemType, con un valor attributeValueScalarType de Item en la información del usuario. Luego, en la información de usuario de su entidad, establezca additionalHeaderFileName en el nombre del encabezado en el que se define la enumeración <=>. Al generar sus archivos de encabezado, mogenerator automáticamente hará que la propiedad tenga el tipo <=>.

Establezco el tipo de atributo como un entero de 16 bits y luego uso esto:

#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

Dado que las enumeraciones están respaldadas por un short estándar, tampoco podría usar el contenedor NSNumber y establecer la propiedad directamente como un valor escalar. Asegúrese de establecer el tipo de datos en el modelo de datos principales como & Quot; Integer 32 & Quot ;.

MyEntity.h

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

@interface myEntity : NSManagedObject

@property (nonatomic) int32_t coreDataEnumStorage;

En otra parte del código

myEntityInstance.coreDataEnumStorage = kEnumThing;

O analizando desde una cadena JSON o cargando desde un archivo

myEntityInstance.coreDataEnumStorage = [myStringOfAnInteger intValue];

El código pegado a continuación funciona para mí, y lo he agregado como ejemplo de trabajo completo. Me gustaría escuchar opiniones sobre este enfoque, ya que planeo usarlo ampliamente en mis aplicaciones.

  • He dejado el @dynamic en su lugar, ya que es satisfecho por el getter / setter nombrado en la propiedad.

  • Según la respuesta de iKenndac, no he anulado los nombres predeterminados de getter / setter.

  • He incluido algunas comprobaciones de rango a través de un NSAssert en los valores válidos de typedef.

  • También he agregado un método para obtener un valor de cadena para el typedef dado.

  • Prefijo constantes con " c " en lugar de " k " ;. Sé el razonamiento detrás de & Quot; k & Quot; (orígenes matemáticos, históricos), pero parece que estoy leyendo el código ESL con él, así que uso " c " ;. Solo una cosa personal.

Hay una pregunta similar aquí: typedef como un tipo de datos Core

Agradecería cualquier aportación sobre este enfoque.

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

He hecho mucho esto y encuentro útil el siguiente formulario:

// 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?

En este caso, la enumeración es bastante simple:

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

y lo llamo pedante, pero uso enumeraciones para los nombres de campo, así:

public enum Field:String {

    case Account = "account"
}

Dado que esto puede ser laborioso para los modelos de datos complejos, escribí un generador de código que consume las entidades / MOM para escupir todas las asignaciones. Mis entradas terminan siendo un diccionario del tipo Tabla / Fila a Enum. Mientras lo hacía, también generé el código de serialización JSON. He hecho esto para modelos muy complejos y ha resultado ser un gran ahorro de tiempo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top