Enums'i Çekirdek Verilerle uygulamanın en iyi yolu


109

Varlığa bir tür özelliği atayabilmem için Temel Veri varlıklarını değerleri numaralandırmanın en iyi yolu nedir? Başka bir deyişle, bir numaraya bağlanmak istediğim Itembir itemTypeözellik ile adlandırılan bir varlığım var, bunu yapmanın en iyi yolu nedir?

Yanıtlar:


130

Değerleri bir numaralandırma ile sınırlamak istiyorsanız, özel erişimciler oluşturmanız gerekecektir. Öyleyse, önce şöyle bir sıralama beyan edersiniz:

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

Ardından, mülkünüz için alıcıları ve ayarlayıcıları bildirin. Mevcut olanları geçersiz kılmak kötü bir fikirdir, çünkü standart erişimciler bir skaler tip yerine bir NSNumber nesnesi beklerler ve bağlamalar veya KVO sistemlerinde herhangi bir şey değerinize erişmeye çalışırsa sorun yaşarsınız.

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

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

Son olarak, + keyPathsForValuesAffecting<Key>itemType değiştiğinde itemTypeRaw için KVO bildirimleri almak üzere uygulamanız gerekir .

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

2
Teşekkürler - çok kötü Core Data bunu yerel olarak desteklemiyor. Demek istediğim: Xcode sınıf dosyaları oluşturur, neden enums olmasın ?
Constantino Tsarouhas

Son kod itemTypeRaw öğesini gözlemlemek istemenizdir. Ancak, itemTypeRaw yerine yalnızca itemType'ı gözlemleyebilirsiniz, değil mi?
Anonim Beyaz

2
Xcode 4.5 ile bunlardan hiçbirine ihtiyacınız yok. Cevabıma bir bak. Sadece numaralandırmayı bir olarak tanımlamanız gerekiyor int16_tve hazırsınız.
Daniel Eggert

79

Bunu daha kolay bir şekilde yapabilirsiniz:

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

@property (nonatomic) Types_t itemType;

Ve modelinizde itemType16 bitlik bir sayı olacak şekilde ayarlayın . Hepsi tamam. Ek kod gerekmez. Sadece her zamanki yerine koy

@dynamic itemType;

NSManagedObjectAlt sınıfınızı oluşturmak için Xcode kullanıyorsanız , " ilkel veri türleri için skaler özellikleri kullan " ayarının işaretli olduğundan emin olun .


4
Hayır, bunun C ++ 11 ile ilgisi yok. ObjC için sabit bir temel tür ile Numaralandırmaları destekleyen clang 3.3'ün bir parçasıdır. Cf clang.llvm.org/docs/…
Daniel Eggert

6
Model sınıfını her yeniden oluşturduğunuzda bu kodu kaybetmekten nasıl kaçınırsınız? Temel etki alanı varlıklarının yeniden oluşturulabilmesi için Kategoriler kullanıyorum.
Rob

2
Bu retain, veri tabanına kaydedilip kaydedilmeyeceği ile değil , bellek yönetimi ile ilgilidir .
Daniel Eggert

2
Rob'a katılıyorum. Bunun tekrar tekrar canlandırılmasını istemiyorum. Kategoriyi tercih ederim.
Kyle Redfearn

3
@Rob Kategorileri bunu yapmanın bir yoludur, ancak bunun yerine mogenerator'ı da kullanabilirsiniz: github.com/rentzsch/mogenerator . Mogenerator, varlık başına 2 sınıf oluşturacaktır; burada bir sınıf, veri modeli değişikliklerinde her zaman üzerine yazılır ve diğer alt sınıflar özel şeyler için sınıflanır ve asla üzerine yazılmaz.
tapmonkey

22

Düşündüğüm alternatif bir yaklaşım, hiç bir enum bildirmek değil, bunun yerine değerleri NSNumber üzerinde kategori yöntemleri olarak bildirmektir.


İlginç. Kesinlikle yapılabilir görünüyor.
Michael Gaylord

parlak fikir! db'de tablo oluşturmaktan çok daha kolay, db'niz bir web hizmetinden doldurulmadıkça muhtemelen en iyisi bir db tablosu kullanmaktır!
TheLearner


Bunu sevdim. Bu yaklaşımı projemde kullanacağım. NSNumber kategorisindeki meta verilerle ilgili diğer tüm meta bilgilerimi de içerebilmeyi seviyorum. (yani dizeleri enum değerlerine bağlama)
DonnaLea

Gerçekten harika bir fikir! Doğrudan JSON, Core Data, vb. Kullanarak dize tanımlayıcıları ilişkilendirmek için çok kullanışlıdır
Gregarious

5

Mogenerator kullanıyorsanız, şuna bir göz atın: https://github.com/rentzsch/mogenerator/wiki/Using-enums-as-types . Sen adlı bir tamsayı 16 özniteliği olabilir itemTypebir ile, attributeValueScalarTypedeğeri Itemkullanıcı bilgilerinde. Ardından, varlığınızın kullanıcı bilgisinde additionalHeaderFileName, Itemnumaralandırmanın tanımlandığı başlığın adını ayarlayın . Başlık dosyalarınızı oluştururken, mogenerator otomatik olarak mülkün Itemtürüne sahip olmasını sağlayacaktır .


2

Öznitelik türünü 16 bitlik tam sayı olarak belirledim ve sonra şunu kullanıyorum:

#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

Numaralandırmalar standart bir kısa ile desteklendiğinden, NSNumber sarmalayıcısını da kullanamaz ve özelliği doğrudan bir skaler değer olarak ayarlayamazsınız. Çekirdek veri modelinde veri türünü "Tamsayı 32" olarak ayarladığınızdan emin olun.

MyEntity.h

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

@interface myEntity : NSManagedObject

@property (nonatomic) int32_t coreDataEnumStorage;

Kodda başka yerde

myEntityInstance.coreDataEnumStorage = kEnumThing;

Veya bir JSON dizesinden ayrıştırma veya bir dosyadan yükleme

myEntityInstance.coreDataEnumStorage = [myStringOfAnInteger intValue];

1

Bunu çok yaptım ve aşağıdaki formu faydalı buldum:

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

Bu durumda sıralama oldukça basittir:

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

ve buna bilgiççe diyoruz, ancak alan adları için enums kullanıyorum, örneğin:

public enum Field:String {

    case Account = "account"
}

Bu karmaşık veri modelleri için zahmetli olabileceğinden, tüm eşlemeleri tükürmek için MOM / varlıkları tüketen bir kod üreteci yazdım. Girdilerim Tablo / Satır'dan Enum türüne kadar bir sözlük haline geliyor. Ben oradayken, JSON serileştirme kodu da ürettim. Bunu çok karmaşık modeller için yaptım ve büyük bir zaman tasarrufu sağladı.


0

Aşağıda yapıştırılan kod benim için çalışıyor ve tam çalışma örneği olarak ekledim. Uygulamalarımda yoğun bir şekilde kullanmayı planladığım için bu yaklaşımla ilgili fikirleri duymak isterim.

  • @Dynamic'i yerinde bıraktım, çünkü daha sonra mülkte adı geçen alıcı / ayarlayıcı tarafından tatmin ediliyor.

  • İKenndac'ın cevabına göre, varsayılan alıcı / ayarlayıcı adlarını geçersiz kılmadım.

  • Typedef geçerli değerlerinde bir NSAssert aracılığıyla bazı aralık denetimi ekledim.

  • Ayrıca verilen typedef için bir dize değeri elde etmek için bir yöntem ekledim.

  • Sabitlerin önüne "k" yerine "c" ekliyorum. "K" nin (matematik kökenleri, tarihsel) arkasındaki mantığı biliyorum, ancak onunla ESL kodunu okuyormuşum gibi geliyor, bu yüzden "c" kullanıyorum. Sadece kişisel bir şey.

Burada benzer bir soru var: typedef bir Çekirdek veri türü olarak

Bu yaklaşımla ilgili her türlü katkıyı takdir ediyorum.

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

Otomatik Oluşturulan Sınıflar için Çözüm

Xcode'un Kod Oluşturucusundan (ios 10 ve üzeri)

"Sınıfınız" adlı bir Varlık oluşturursanız, Xcode otomatik olarak "Sınıf Tanımı" nı varsayılan olarak "Veri Modeli Denetçisi" nde bir Codegen türü seçecektir. bu, aşağıdaki sınıfları oluşturacaktır:

Swift versiyonu:

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

Objective-C sürümü:

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

  #import "YourClass+CoreDataProperties.h"

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

Xcode'da "Sınıf Tanımı" yerine Codegen seçeneğinden "Kategori / Uzantı" yı seçeceğiz.

Şimdi, bir enum eklemek istiyorsak, gidip otomatik olarak oluşturulan sınıfınız için başka bir uzantı oluşturun ve aşağıdaki gibi enum tanımlarınızı buraya ekleyin:

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

Şimdi, değerleri bir numaralandırmayla sınırlamak istiyorsanız özel erişimciler oluşturabilirsiniz. Lütfen soru sahibinin kabul ettiği cevabı kontrol edin . Veya, aşağıda olduğu gibi döküm operatörünü kullanarak açık bir şekilde dönüştürme yöntemiyle ayarlarken numaralandırmalarınızı dönüştürebilirsiniz:

model.yourEnumProperty = (int16_t)YourEnumTypeStarted;

Ayrıca kontrol edin

Xcode otomatik alt sınıf oluşturma

Xcode artık modelleme aracında NSManagedObject alt sınıflarının otomatik olarak oluşturulmasını desteklemektedir. Varlık denetçisinde:

Manuel / Hiçbiri varsayılan ve önceki davranıştır; bu durumda, kendi alt sınıfınızı uygulamalı veya NSManagedObject kullanmalısınız. Kategori / Uzantı, ClassName + CoreDataGeneratedProperties gibi adlı bir dosyada bir sınıf uzantısı oluşturur. Ana sınıfı bildirmeniz / uygulamanız gerekir (Obj-C'de ise, bir başlık aracılığıyla uzantı ClassName.h adlı dosyayı içe aktarabilir). Sınıf Tanımı, ClassName + CoreDataClass gibi alt sınıf dosyalarının yanı sıra Kategori / Uzantı için oluşturulan dosyaları da oluşturur. Oluşturulan dosyalar DerivedData içine yerleştirilir ve model kaydedildikten sonra ilk derlemede yeniden oluşturulur. Ayrıca Xcode tarafından indekslenirler, bu nedenle referanslara komut tıklamak ve dosya adına göre hızlı açmak çalışır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.