コアデータで列挙型を実装する最良の方法


109

エンティティにタイププロパティを割り当てることができるように、コアデータエンティティを列挙値にバインドする最良の方法は何ですか?言い換えれば、私はと呼ばれるエンティティ持っているItemitemType私は列挙型にバインドしたいというプロパティを、このついて行くの最善の方法は何ですか。

回答:


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>itemTypeが変更されたときにitemTypeRawのKVO通知を受け取るように実装する必要があります。

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

2
ありがとう—非常に悪いCore Dataはこれをネイティブでサポートしていません。つまり、Xcodeはクラスファイルを生成しますが、なぜenumですか?
コンスタンティーノサロウハ

最後のコードは、アイテムitemTypeRawを監視する場合です。ただし、itemTypeRawの代わりにitemItemTypeを単純に観察できますか?
匿名のホワイト

2
Xcode 4.5では、これは必要ありません。私の答えを見てください。列挙型をとして定義するだけで、設定が完了しint16_tます。
Daniel Eggert

79

あなたはこの方法で、もっと簡単に行うことができます:

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

@property (nonatomic) Types_t itemType;

また、モデルではitemType、16ビットの数値に設定します。すべて完了。追加のコードは必要ありません。いつもの中に入れるだけ

@dynamic itemType;

Xcodeを使用してNSManagedObjectサブクラスを作成している場合は、「プリミティブデータ型にスカラープロパティを使用する」設定がオンになっていることを確認してください。


4
いいえ、これはC ++ 11とは関係ありません。これは、Cjg 3.3の一部であり、ObjCの基本となる型固定された列挙をサポートしています。CF clang.llvm.org/docs/...
ダニエルEggertの

6
モデルクラスを再生成するたびにこのコードが失われないようにするにはどうすればよいですか?コアドメインエンティティを再生成できるように、カテゴリを使用しています。
Rob

2
retain、メモリ管理に関連していないそれはデータベースかに格納されますかどうか。
Daniel Eggert、2013

2
私はロブに同意します。これを何度も再生する必要はありません。私はカテゴリーが好きです。
Kyle Redfearn、2013

3
@Robカテゴリーはそれを行う方法ですが、代わりにmogeneratorを使用することもできます:github.com/rentzsch/mogenerator。Mogeneratorはエンティティごとに2つのクラスを生成します。1つのクラスは常にデータモデルの変更時に上書きされ、他のサブクラスはカスタムクラス用にそのクラスに上書きされ、上書きされることはありません。
Tapmonkey 2014年

22

私が検討している別のアプローチは、列挙型をまったく宣言するのではなく、代わりに値をNSNumberのカテゴリメソッドとして宣言することです。


面白い。それは間違いなく実行可能のようです。
マイケルゲイロード

素晴らしいアイデア!dbにテーブルを作成するよりもはるかに簡単です。dbがWebサービスから入力されない限り、おそらくdbテーブルを使用するのが最善です。
TheLearner '10 / 10/04


私はそれが好きです。私のプロジェクトでは、このアプローチを使用します。NSNumberカテゴリ内のメタデータに関する他のすべてのメタ情報も含めることができるのが好きです。(つまり、文字列を列挙値にリンクする)
DonnaLea '28年

本当に素晴らしいアイデアです!などJSON、コアデータに直接使用して、文字列識別子を関連付けるために非常に便利
社交

5

mogeneratorを使用している場合は、https//github.com/rentzsch/mogenerator/wiki/Using-enums-as-typesをご覧ください。ユーザー情報にの値をitemType持つと呼ばれる整数16属性を持つことができます。次に、エンティティのユーザー情報で、列挙型が定義されているヘッダーの名前に設定します。ヘッダーファイルを生成すると、mogeneratorは自動的にプロパティにタイプを設定します。attributeValueScalarTypeItemadditionalHeaderFileNameItemItem


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

列挙型は標準のshortでサポートされているため、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 /エンティティを使用してすべてのマッピングを吐き出すコードジェネレーターを作成しました。私の入力は、テーブル/行から列挙型への辞書になります。その間、JSONシリアル化コードも生成しました。非常に複雑なモデルでこれを実行しましたが、時間を大幅に節約できることがわかりました。


0

以下に貼り付けたコードは私にとってはうまくいき、私はそれを完全に機能する例として追加しました。アプリ全体で幅広く使用するつもりなので、このアプローチについて意見を伺いたい。

  • @dynamicはそのままにしておきます。プロパティで指定されたゲッター/セッターによって満たされるからです。

  • iKenndacによる回答に従って、デフォルトのゲッター/セッター名を上書きしていません。

  • typedefの有効な値にNSAssertを介していくつかの範囲チェックを含めました。

  • 指定されたtypedefの文字列値を取得するメソッドも追加しました。

  • 定数の前に「k」ではなく「c」を付けます。私は「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

0

自動生成クラスのソリューション

Xcodeのコードジェネレーターから(iOS 10以降)

「YourClass」という名前のエンティティを作成すると、Xcodeは自動的に「クラス定義」を「データモデルインスペクタ」でのCodegenタイプとしてデフォルトで選択します。これにより、以下のクラスが生成されます。

Swiftバージョン:

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

Xcodeの「クラス定義」ではなく、Codegenオプションから「Category / Extension」を選択します。

ここで、列挙型を追加する場合は、自動生成されたクラスに別の拡張機能を作成し、以下のように列挙型定義をここに追加します。

// 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サブクラスの自動生成をサポートするようになりました。エンティティインスペクターで:

Manual / Noneがデフォルトであり、以前の動作です。この場合、独自のサブクラスを実装するか、NSManagedObjectを使用する必要があります。Category / Extensionは、ClassName + CoreDataGeneratedPropertiesのような名前のファイルにクラス拡張を生成します。メインクラスを宣言/実装する必要があります(Obj-Cの場合、ヘッダーを介して、拡張機能がClassName.hという名前でインポートできます)。クラス定義は、ClassName + CoreDataClassのような名前のサブクラスファイルと、Category / Extension用に生成されたファイルを生成します。生成されたファイルはDerivedDataに配置され、モデルが保存された後の最初のビルドで再構築されます。また、Xcodeによってインデックスが作成されるため、参照をコマンドクリックしてファイル名で高速に開くことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.