Найкращий спосіб впровадити Enums за допомогою основних даних


109

Який найкращий спосіб прив’язати суб'єкти основних даних до перерахування значень, щоб я міг призначити властивість типу об'єкту? Іншими словами, у мене є об'єкт , званий Itemз itemTypeвластивістю , що я хочу бути пов'язаний з перерахуванням, що це кращий спосіб йти про це.

Відповіді:


130

Якщо вам потрібно обмежити значення перерахунком, вам доведеться створити спеціальні аксесуари. Отже, спочатку ви оголосите перерахунок, як-от так:

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"];
}

2
Дякую - занадто погані основні дані не підтримують це в початковому порядку. Я маю на увазі: Xcode створює файли класів, чому б не enums?
Константіно Царухас

Останній код - якщо ви хочете спостерігати за item itemTypeRaw. Однак ви можете просто спостерігати пункт itemType замість itemTypeRaw правда?
Анонімний Білий

2
З Xcode 4.5 вам нічого цього не потрібно. Погляньте на мою відповідь. Вам просто потрібно визначити перерахунок як int16_tі ви налаштовані.
Даніель Еггерт

79

Це можна зробити так, простіше:

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

@property (nonatomic) Types_t itemType;

А у вашій моделі встановлено itemType16-бітове число. Готово. Не потрібен додатковий код. Просто покладіть своє звичне

@dynamic itemType;

Якщо ви використовуєте Xcode для створення свого NSManagedObjectпідкласу, переконайтеся, що встановлено прапорець " використовувати скалярні властивості для примітивних типів даних ".


4
Ні, це не має нічого спільного з C ++ 11. Це частина кланг 3.3, що підтримує перерахування з фіксованим базовим типом для ObjC. Cf clang.llvm.org/docs/…
Даніель Еггерт

6
Як уникнути втрати цього коду кожного разу, коли ви регенеруєте клас моделі? Я використовував категорії, щоб можна було відновити основні об'єкти домену.
Роб

2
Це retainпов'язано з керуванням пам'яттю, не залежно від того, зберігається він у базі даних чи ні.
Даніель Еггерт

2
Я згоден з Робом. Я не хочу, щоб це потрібно було відновлювати знову і знову. Я віддаю перевагу категорії.
Кайл Редфіарн

3
@Rob Категорії - це спосіб це зробити, але замість цього ви можете також використовувати mogenerator: github.com/rentzsch/mogenerator . Mogenerator генерує 2 класи на сутність, де один клас завжди буде перезаписаний на зміну моделі даних, а інші підкласи, які класують для користувацьких матеріалів і ніколи не перезаписуються.
tapmonkey

22

Альтернативний підхід, який я розглядаю, - не оголошувати перерахунок взагалі, а замість того, щоб оголосити значення як методи категорії в NSNumber.


Цікаво. Це, безумовно, здається виконаним.
Майкл Гейлорд

геніальна ідея! набагато простіше, ніж створення таблиць в db, якщо тільки ваш db заповнений з веб-сервісу, то, ймовірно, найкраще використовувати таблицю db!
TheLearner


Мені це подобається. Я буду використовувати такий підхід у своєму проекті. Мені подобається, що я можу також містити всю свою іншу мета-інформацію про метадані в категорії NSNumber. (тобто зв'язування рядків із значеннями перерахунків)
DonnaLea,

Дійсно чудова ідея! Дуже корисно для асоціації рядкових ідентифікаторів, використовуючи безпосередньо в JSON, Core Data і т.д.
Gregarious

5

Якщо ви використовуєте mogenerator, подивіться на це: https://github.com/rentzsch/mogenerator/wiki/Using-enums-as-types . Ви можете викликати атрибут Integer 16 itemTypeзі attributeValueScalarTypeзначенням Itemв інформації про користувача. Потім у відомості про користувача для вашої сутності встановіть additionalHeaderFileNameім'я заголовка, у якому Itemвизначено перерахунок. Під час генерації файлів заголовків mogenerator автоматично зробить властивість Itemтипом.


2

Я встановлюю тип атрибута як 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

1

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

MyEntity.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];

1

Я дуже багато зробив і вважаю корисною наступну форму:

// 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. Я робив це для дуже складних моделей, і це виявилося великим заощадженням часу.


0

Код, вставлений нижче, працює для мене, і я додав його як повний робочий приклад. Мені хотілося б почути думки щодо цього підходу, оскільки я планую широко використовувати його у своїх програмах.

  • Я залишив @dynamic на місці, оскільки його влаштовує getter / setter, названий у власність.

  • Відповідно до відповіді iKenndac, я не перемінив імена getter / setter за замовчуванням.

  • Я включив деяку перевірку діапазону за допомогою NSAssert на дійсні значення typedef.

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

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

Тут є подібне питання: typedef як тип даних Core

Я вдячний за будь-який внесок у цей підхід.

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

0

Рішення для автоматизованих класів

від генератора коду Xcode (ios 10 і вище)

Якщо ви створите сутність під назвою "YourClass", Xcode автоматично вибере "Визначення класу" як типовий тип кодена у "Інспектор моделі даних". це створить класи нижче:

Швидка версія:

// YourClass+CoreDataClass.swift
  @objc(YourClass)
  public class YourClass: NSManagedObject {
  }

Версія Objective-C:

// YourClass+CoreDataClass.h
  @interface YourClass : NSManagedObject
  @end

  #import "YourClass+CoreDataProperties.h"

  // YourClass+CoreDataClass.m
  #import "YourClass+CoreDataClass.h"
  @implementation YourClass
  @end

Ми виберемо "Категорія / Розширення" з параметра Codegen замість "Визначення класу" в Xcode.

Тепер, якщо ми хочемо додати перерахунок, перейдіть і створіть ще одне розширення для вашого автоматично сформованого класу та додайте тут свої визначення переліків, як нижче:

// YourClass+Extension.h

#import "YourClass+CoreDataClass.h" // That was the trick for me!

@interface YourClass (Extension)

@end


// YourClass+Extension.m

#import "YourClass+Extension.h"

@implementation YourClass (Extension)

typedef NS_ENUM(int16_t, YourEnumType) {
    YourEnumTypeStarted,
    YourEnumTypeDone,
    YourEnumTypePaused,
    YourEnumTypeInternetConnectionError,
    YourEnumTypeFailed
};

@end

Тепер ви можете створити спеціальні аксесуари, якщо ви хочете обмежити значення перерахунком. Перевірте прийняту відповідь від власника запитання . Або ви можете конвертувати ваші перерахунки, встановлюючи їх явно методом перетворення, використовуючи оператор лиття, як показано нижче:

model.yourEnumProperty = (int16_t)YourEnumTypeStarted;

Також перевірте

Автоматичне генерування підкласу Xcode

Тепер Xcode підтримує автоматичну генерацію підкласів NSManagedObject в інструменті моделювання. У суб'єктному інспекторі:

Посібник / Нічого - це типова та попередня поведінка; у цьому випадку слід реалізувати власний підклас або використовувати NSManagedObject. Категорія / Розширення генерує розширення класу у файлі з назвою ClassName + CoreDataGeneratedProperties. Вам потрібно оголосити / реалізувати основний клас (якщо в Obj-C через заголовок розширення може імпортувати назву ClassName.h). Визначення класу генерує файли підкласу, названі як ClassName + CoreDataClass, а також файли, згенеровані для категорії / розширення. Згенеровані файли розміщуються в DerivedData та відновлюються під час першої збірки після збереження моделі. Вони також індексуються Xcode, тому команда клацання посилання та швидке відкриття за назвою файлу працює.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.