핵심 데이터로 Enum을 구현하는 가장 좋은 방법


109

엔터티에 유형 속성을 할당 할 수 있도록 핵심 데이터 엔터티를 열거 형 값에 바인딩하는 가장 좋은 방법은 무엇입니까? 즉, 내가이라는 실체가 ItemitemType내가 열거에 구속하려는 속성을, 이것에 대해가는 가장 좋은 방법은 무엇인가.

답변:


130

값을 열거 형으로 제한하려면 사용자 지정 접근자를 만들어야합니다. 따라서 먼저 다음과 같이 열거 형을 선언합니다.

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

그런 다음 속성에 대한 getter 및 setter를 선언하십시오. 표준 접근자가 스칼라 유형이 아닌 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는 클래스 파일을 생성하는데 왜 enums가 아니죠 ?
Constantino Tsarouhas 2012

마지막 코드는 itemTypeRaw 항목을 관찰하려는 경우입니다. 그러나 itemTypeRaw 대신 itemType 항목을 관찰 할 수 있습니다.
익명 화이트

2
Xcode 4.5에서는이 중 어떤 것도 필요하지 않습니다. 내 대답을 살펴보십시오. 열거 형을로 정의하면 int16_t됩니다.
Daniel Eggert 2012

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과 관련이 없습니다. ObjC에 대한 고정 된 기본 유형으로 열거를 지원하는 clang 3.3의 일부입니다 . Cf clang.llvm.org/docs/…
Daniel Eggert

6
모델 클래스를 재생성 할 때마다이 코드가 손실되는 것을 어떻게 방지 할 수 있습니까? 핵심 도메인 엔터티를 다시 생성 할 수 있도록 범주를 사용하고 있습니다.
Rob

2
이는 데이터베이스에 저장되는지 여부가 아니라retain 메모리 관리와 관련이 있습니다.
Daniel Eggert 2013-04-05

2
나는 Rob에 동의합니다. 나는 이것이 반복해서 재생되는 것을 원하지 않습니다. 나는 카테고리를 선호합니다.
Kyle Redfearn

3
@Rob Categories는이를 수행하는 방법이지만 대신 mogenerator : github.com/rentzsch/mogenerator를 사용할 수도 있습니다 . Mogenerator는 엔티티 당 2 개의 클래스를 생성합니다. 여기서 한 클래스는 데이터 모델 변경시 항상 덮어 쓰이고 다른 하위 클래스는 사용자 지정 항목에 대해 해당 클래스를 덮어 쓰지 않고 덮어 쓰지 않습니다.
tapmonkey

22

내가 고려하고있는 다른 접근 방식은 열거 형을 전혀 선언하지 않고 대신 NSNumber의 범주 메서드로 값을 선언하는 것입니다.


흥미 롭군. 확실히 할 수있는 것 같습니다.
Michael Gaylord

훌륭한 아이디어! db가 웹 서비스에서 채워지지 않는 한 db 테이블을 만드는 것보다 훨씬 쉽습니다. db 테이블을 사용하는 것이 가장 좋습니다!
TheLearner


나는 그것을 좋아한다. 내 프로젝트에서이 접근 방식을 사용할 것입니다. NSNumber 카테고리 내의 메타 데이터에 대한 다른 모든 메타 정보도 포함 할 수 있다는 점이 마음에 듭니다. (즉, 문자열을 열거 형 값에 연결)
DonnaLea

정말 좋은 생각입니다! JSON, Core Data 등에서 직접 사용하여 문자열 식별자를 연결하는 데 매우 유용합니다.
Gregarious

5

mogenerator를 사용하는 경우 https://github.com/rentzsch/mogenerator/wiki/Using-enums-as-types를 살펴보십시오 . 당신은라는 정수 (16) 속성을 가질 수 itemTypeA를, 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

열거 형은 표준 short로 뒷받침되기 때문에 NSNumber 래퍼를 사용하지 않고 속성을 스칼라 값으로 직접 설정할 수도 있습니다. 핵심 데이터 모델의 데이터 유형을 "Integer 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 / 엔티티를 사용하는 코드 생성기를 작성했습니다. 내 입력은 Table / Row에서 Enum 유형으로의 사전이됩니다. 거기에있는 동안 JSON 직렬화 코드도 생성했습니다. 매우 복잡한 모델에 대해이 작업을 수행했으며 시간을 크게 절약 할 수있었습니다.


0

아래에 붙여 넣은 코드가 저에게 효과적이며 전체 작업 예제로 추가했습니다. 이 접근 방식을 앱 전체에서 광범위하게 사용할 계획이므로이 접근 방식에 대한 의견을 듣고 싶습니다.

  • @dynamic은 속성에 명명 된 getter / setter가 만족하므로 그대로 두었습니다.

  • iKenndac의 답변에 따라 기본 getter / setter 이름을 재정의하지 않았습니다.

  • typedef 유효한 값에 대해 NSAssert를 통해 일부 범위 검사를 포함했습니다.

  • 또한 주어진 typedef에 대한 문자열 값을 얻는 방법을 추가했습니다.

  • 상수 앞에 "k"가 아닌 "c"를 붙입니다. 나는 "k"(수학의 기원, 역사)에 대한 이유를 알고 있지만, ESL 코드를 읽는 것처럼 느껴져서 "c"를 사용합니다. 개인적인 일입니다.

비슷한 질문이 있습니다. typedef as a Core data type

이 접근 방식에 대한 의견을 보내 주시면 감사하겠습니다.

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는 "Data Model Inspector"에서 기본 Codegen 유형으로 "Class Definition"을 자동으로 선택합니다. 그러면 아래와 같은 클래스가 생성됩니다.

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의 "Class Definition"대신 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 하위 클래스의 자동 생성을 지원합니다. 엔티티 검사기에서 :

수동 / 없음이 기본값이며 이전 동작입니다. 이 경우 자체 서브 클래스를 구현하거나 NSManagedObject를 사용해야합니다. 범주 / 확장자는 ClassName + CoreDataGeneratedProperties와 같은 이름의 파일에 클래스 확장을 생성합니다. 메인 클래스를 선언 / 구현해야합니다 (Obj-C에서 헤더를 통해 확장은 ClassName.h라는 이름을 가져올 수 있음). Class Definition은 ClassName + CoreDataClass와 같은 이름의 하위 클래스 파일과 Category / Extension에 대해 생성 된 파일을 생성합니다. 생성 된 파일은 DerivedData에 배치되고 모델이 저장된 후 첫 번째 빌드에서 다시 빌드됩니다. 또한 Xcode에 의해 색인화되므로 참조를 명령 클릭하고 파일 이름으로 빠르게 열기가 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.