Cách tốt nhất để triển khai Enums với dữ liệu cốt lõi


109

Cách tốt nhất để liên kết các thực thể Dữ liệu lõi với các giá trị enum để tôi có thể gán thuộc tính kiểu cho thực thể là gì? Nói cách khác, tôi có một thực thể được gọi Itemvới một thuộc itemTypetính mà tôi muốn liên kết với một enum, cách tốt nhất để thực hiện điều này là gì.

Câu trả lời:


130

Bạn sẽ phải tạo các trình truy cập tùy chỉnh nếu bạn muốn giới hạn các giá trị trong một enum. Vì vậy, trước tiên bạn khai báo một enum, như sau:

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

Sau đó, khai báo getters và setters cho tài sản của bạn. Đó là một ý tưởng tồi nếu ghi đè những cái hiện có, vì những người truy cập tiêu chuẩn mong đợi một đối tượng NSNumber hơn là một loại vô hướng và bạn sẽ gặp rắc rối nếu bất kỳ thứ gì trong hệ thống ràng buộc hoặc KVO thử và truy cập giá trị của bạn.

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

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

Cuối cùng, bạn nên triển khai + keyPathsForValuesAffecting<Key>để bạn nhận được thông báo KVO cho itemTypeRaw khi itemType thay đổi.

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

2
Cảm ơn bạn - Dữ liệu lõi quá tệ không hỗ trợ điều này. Ý tôi là: Xcode tạo ra các tệp lớp, tại sao không phải enums?
Constantino Tsarouhas,

Mã cuối cùng là nếu bạn muốn quan sát item itemTypeRaw. Tuy nhiên, bạn có thể đơn giản quan sát item itemType thay vì itemTypeRaw đúng không?
Anonymous White,

2
Với Xcode 4.5, bạn không cần bất kỳ điều này. Hãy xem câu trả lời của tôi. Bạn chỉ cần xác định enum là một int16_tvà bạn đã thiết lập.
Daniel Eggert

79

Bạn có thể làm theo cách này, cách đơn giản hơn:

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

@property (nonatomic) Types_t itemType;

Và trong mô hình của bạn, hãy đặt thành itemTypemột số 16 bit. Tất cả đã được làm xong. Không cần mã bổ sung. Chỉ cần đặt bình thường của bạn

@dynamic itemType;

Nếu bạn đang sử dụng Xcode để tạo NSManagedObjectlớp con của mình , hãy đảm bảo rằng cài đặt " sử dụng thuộc tính vô hướng cho kiểu dữ liệu nguyên thủy " được chọn.


4
Không, điều này không liên quan gì đến C ++ 11. Nó là một phần của clang 3.3 hỗ trợ Enumerations với một kiểu cơ bản cố định cho ObjC. Cf clang.llvm.org/docs/…
Daniel Eggert

6
Làm cách nào để bạn tránh mất mã này mỗi khi bạn tạo lại lớp mô hình? Tôi đã sử dụng Danh mục để có thể tạo lại các thực thể miền lõi.
Rob

2
Liên retainquan đến quản lý bộ nhớ, không phải là nó có được lưu trữ vào cơ sở dữ liệu hay không.
Daniel Eggert

2
Tôi đồng ý với Rob. Tôi không muốn điều này phải được tạo lại nhiều lần. Tôi thích thể loại hơn.
Kyle Redfearn

3
@Rob Danh mục là một cách để làm điều đó, nhưng thay vào đó, bạn cũng có thể sử dụng mogenerator: github.com/rentzsch/mogenerator . Mogenerator sẽ tạo ra 2 lớp cho mỗi thực thể, trong đó một lớp sẽ luôn bị ghi đè khi thay đổi mô hình dữ liệu và các lớp con khác mà lớp đó dành cho nội dung tùy chỉnh và không bao giờ bị ghi đè.
tapmonkey

22

Một cách tiếp cận thay thế mà tôi đang xem xét không phải là khai báo một enum mà thay vào đó khai báo các giá trị dưới dạng các phương thức danh mục trên NSNumber.


Hấp dẫn. Nó chắc chắn có vẻ khả thi.
Michael Gaylord

Ý tưởng tuyệt vời! dễ dàng hơn nhiều so với việc tạo bảng trong db, trừ khi db của bạn được điền từ một dịch vụ web thì tốt nhất bạn nên sử dụng bảng db!
TheLearner

6
Dưới đây là một ví dụ: renovatioboy.wordpress.com/2011/10/06/...
ardochhigh

Tôi thích nó. Tôi sẽ sử dụng cách tiếp cận này trong dự án của mình. Tôi thích rằng tôi cũng có thể chứa tất cả thông tin meta khác của mình về dữ liệu meta trong danh mục NSNumber. (tức là liên kết chuỗi các giá trị enum)
DonnaLea

Ý tưởng thực sự tuyệt vời! Rất hữu ích để liên kết các số nhận dạng chuỗi, sử dụng trực tiếp trong JSON, Dữ liệu cốt lõi, v.v.
Gregarious

5

Nếu bạn đang sử dụng mogenerator, hãy xem phần này: https://github.com/rentzsch/mogenerator/wiki/Using-enums-as-types . Bạn có thể có một thuộc tính Integer 16 được gọi itemType, với attributeValueScalarTypegiá trị Itemtrong thông tin người dùng. Sau đó, trong thông tin người dùng cho thực thể của bạn, hãy đặt additionalHeaderFileNametên của tiêu đề mà Itemenum được xác định. Khi tạo tệp tiêu đề của bạn, mogenerator sẽ tự động đặt thuộc tính có Itemloại.


2

Tôi đặt loại thuộc tính là số nguyên 16 bit, sau đó sử dụng điều này:

#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

Vì enums được hỗ trợ bởi một short tiêu chuẩn nên bạn cũng không thể sử dụng trình bao bọc NSNumber và đặt thuộc tính trực tiếp dưới dạng giá trị vô hướng. Đảm bảo đặt kiểu dữ liệu trong mô hình dữ liệu cốt lõi là "Số nguyên 32".

MyEntity.h

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

@interface myEntity : NSManagedObject

@property (nonatomic) int32_t coreDataEnumStorage;

Ở những nơi khác trong mã

myEntityInstance.coreDataEnumStorage = kEnumThing;

Hoặc phân tích cú pháp từ một chuỗi JSON hoặc tải từ một tệp

myEntityInstance.coreDataEnumStorage = [myStringOfAnInteger intValue];

1

Tôi đã làm điều này rất nhiều và thấy biểu mẫu sau hữu ích:

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

Trong trường hợp này, enum khá đơn giản:

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

và gọi nó là pedantic, nhưng tôi sử dụng enum cho tên trường, như thế này:

public enum Field:String {

    case Account = "account"
}

Vì điều này có thể tốn nhiều công sức đối với các mô hình dữ liệu phức tạp, tôi đã viết một trình tạo mã sử dụng MOM / thực thể để tạo ra tất cả các ánh xạ. Đầu vào của tôi kết thúc là một từ điển từ kiểu Bảng / Hàng đến kiểu Enum. Trong khi tôi ở đó, tôi cũng đã tạo mã tuần tự hóa JSON. Tôi đã làm điều này cho các mô hình rất phức tạp và nó tiết kiệm thời gian rất nhiều.


0

Đoạn mã được dán bên dưới phù hợp với tôi và tôi đã thêm nó làm ví dụ hoạt động đầy đủ. Tôi muốn nghe ý kiến ​​về cách tiếp cận này, vì tôi dự định sử dụng nó rộng rãi trong các ứng dụng của mình.

  • Tôi đã để @dynamic tại chỗ, vì sau đó nó được thỏa mãn bởi getter / setter có tên trong thuộc tính.

  • Theo câu trả lời của iKenndac, tôi đã không ghi đè các tên getter / setter mặc định.

  • Tôi đã bao gồm một số kiểm tra phạm vi thông qua NSAssert về các giá trị hợp lệ của typedef.

  • Tôi cũng đã thêm một phương thức để lấy giá trị chuỗi cho typedef đã cho.

  • Tôi đặt tiền tố hằng số bằng "c" thay vì "k". Tôi biết lý do đằng sau "k" (nguồn gốc toán học, lịch sử), nhưng có cảm giác như tôi đang đọc mã ESL với nó, vì vậy tôi sử dụng "c". Chỉ là một việc cá nhân.

Có một câu hỏi tương tự ở đây: typedef làm kiểu dữ liệu Core

Tôi đánh giá cao bất kỳ đầu vào nào về cách tiếp cận này.

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

Giải pháp cho các lớp học được tạo tự động

từ Trình tạo mã của Xcode (ios 10 trở lên)

Nếu bạn tạo một Thực thể có tên là "YourClass", Xcode sẽ tự động chọn "Định nghĩa lớp" làm loại Codegen mặc định tại "Data Model Inspector". điều này sẽ tạo ra các lớp bên dưới:

Phiên bản Swift:

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

Phiên bản Objective-C:

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

  #import "YourClass+CoreDataProperties.h"

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

Chúng tôi sẽ chọn "Danh mục / Phần mở rộng" từ tùy chọn Codegen thay vì "Định nghĩa Lớp" trong Xcode.

Bây giờ, nếu chúng ta muốn thêm một enum, hãy tạo một phần mở rộng khác cho lớp được tạo tự động của bạn và thêm các định nghĩa enum của bạn tại đây như bên dưới:

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

Bây giờ, bạn có thể tạo các trình truy cập tùy chỉnh nếu bạn muốn giới hạn các giá trị trong một enum. Vui lòng kiểm tra câu trả lời được chấp nhận bởi chủ sở hữu câu hỏi . Hoặc bạn có thể chuyển đổi enum của mình trong khi bạn đặt chúng bằng phương pháp chuyển đổi rõ ràng bằng cách sử dụng toán tử truyền như dưới đây:

model.yourEnumProperty = (int16_t)YourEnumTypeStarted;

Cũng kiểm tra

Tạo lớp con tự động Xcode

Xcode hiện hỗ trợ tạo tự động các lớp con NSManagedObject trong công cụ mô hình hóa. Trong thanh tra thực thể:

Thủ công / Không là hành vi mặc định và trước đó; trong trường hợp này, bạn nên triển khai lớp con của riêng mình hoặc sử dụng NSManagedObject. Category / Extension tạo một phần mở rộng lớp trong một tệp có tên như ClassName + CoreDataGeneratedProperties. Bạn cần khai báo / triển khai lớp chính (nếu trong Obj-C, thông qua tiêu đề, phần mở rộng có thể nhập có tên ClassName.h). Định nghĩa lớp tạo ra các tệp lớp con có tên như ClassName + CoreDataClass cũng như các tệp được tạo cho Danh mục / Phần mở rộng. Các tệp đã tạo được đặt trong DerivedData và được tạo lại trên bản dựng đầu tiên sau khi mô hình được lưu. Chúng cũng được lập chỉ mục bởi Xcode, vì vậy việc nhấp chuột vào các tài liệu tham khảo và mở nhanh theo tên tệp sẽ hoạt động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.