objective-c 使用核心数据实现枚举的最佳方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1624297/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 22:24:46  来源:igfitidea点击:

Best way to implement Enums with Core Data

iphoneobjective-ccocoacocoa-touchcore-data

提问by Michael Gaylord

What is the best way to bind Core Data entities to enum values so that I am able to assign a type property to the entity? In other words, I have an entity called Itemwith an itemTypeproperty that I want to be bound to an enum, what is the best way of going about this.

将 Core Data 实体绑定到枚举值以便我能够为实体分配类型属性的最佳方法是什么?换句话说,我有一个实体,它Item带有一个itemType我想绑定到枚举的属性,解决这个问题的最佳方法是什么。

回答by iKenndac

You'll have to create custom accessors if you want to restrict the values to an enum. So, first you'd declare an enum, like so:

如果要将值限制为枚举,则必须创建自定义访问器。所以,首先你要声明一个枚举,像这样:

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

Then, declare getters and setters for your property. It's a bad idea to override the existing ones, since the standard accessors expect an NSNumber object rather than a scalar type, and you'll run into trouble if anything in the bindings or KVO systems try and access your value.

然后,为您的属性声明 getter 和 setter。覆盖现有访问器是一个坏主意,因为标准访问器需要 NSNumber 对象而不是标量类型,如果绑定或 KVO 系统中的任何内容尝试访问您的值,您就会遇到麻烦。

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

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

Finally, you should implement + keyPathsForValuesAffecting<Key>so you get KVO notifications for itemTypeRaw when itemType changes.

最后,您应该实施+ keyPathsForValuesAffecting<Key>以便在 itemType 更改时获得 itemTypeRaw 的 KVO 通知。

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

回答by Daniel Eggert

You can do this way, way simpler:

你可以这样做,方式更简单:

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

@property (nonatomic) Types_t itemType;

And in your model, set itemTypeto be a 16 bit number. All done. No additional code needed. Just put in your usual

在您的模型中,设置itemType为 16 位数字。全部完成。不需要额外的代码。只需放入你平常的

@dynamic itemType;

If you're using Xcode to create your NSManagedObjectsubclass, make sure that the "use scalar properties for primitive data types" setting is checked.

如果您使用 Xcode 创建NSManagedObject子类,请确保选中“对原始数据类型使用标量属性”设置。

回答by Mike Abdullah

An alternative approach I'm considering is not to declare an enum at all, but to instead declare the values as category methods on NSNumber.

我正在考虑的另一种方法是根本不声明枚举,而是将值声明为 NSNumber 上的类别方法。

回答by jfla

If you're using mogenerator, have a look at this: https://github.com/rentzsch/mogenerator/wiki/Using-enums-as-types. You can have an Integer 16 attribute called itemType, with a attributeValueScalarTypevalue of Itemin the user info. Then, in the user info for your entity, set additionalHeaderFileNameto the name of the header that the Itemenum is defined in. When generating your header files, mogenerator will automatically make the property have the Itemtype.

如果您使用的是 mogenerator,请查看:https: //github.com/rentzsch/mogenerator/wiki/Using-enums-as-types。您可以拥有一个名为 的 Integer 16 属性itemType,其attributeValueScalarTypeItem在用户信息中。然后,在您的实体的用户信息中,设置为定义枚举additionalHeaderFileName的头的名称Item。生成头文件时,mogenerator 将自动使该属性具有该Item类型。

回答by malhal

I set the attribute type as 16 bit integer then use this:

我将属性类型设置为 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

回答by Chris Conover

I have done this a lot and find the following form to be useful:

我已经做了很多,发现以下表格很有用:

// accountType
public var account:AccountType {
    get {
        willAccessValueForKey(Field.Account.rawValue)
        defer { didAccessValueForKey(Field.Account.rawValue) }
        return primitiveAccountType.flatMap { AccountType(rawValue: 
public enum AccountType: String {
    case New = "new"
    case Registered = "full"
}
) } ?? .New } set { willChangeValueForKey(Field.Account.rawValue) defer { didChangeValueForKey(Field.Account.rawValue) } primitiveAccountType = newValue.rawValue }} @NSManaged private var primitiveAccountType: String?

In this case, the enum is pretty simple:

在这种情况下,枚举非常简单:

public enum Field:String {

    case Account = "account"
}

and call it pedantic, but I use enums for field names, like this:

并称之为迂腐,但我使用枚举作为字段名称,如下所示:

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

@interface myEntity : NSManagedObject

@property (nonatomic) int32_t coreDataEnumStorage;

Since this can get laborious for complex data models, I wrote a code generator that consumes the MOM / entities to spit out all the mappings. My inputs end up being a dictionary from Table/Row to Enum type. While I was at it, I also generated JSON serialization code. I've done this for very complex models and it has turned out to be a big time saver.

由于这对于复杂的数据模型来说可能会很费力,因此我编写了一个代码生成器,它使用 MOM/实体来吐出所有映射。我的输入最终成为从 Table/Row 到 Enum 类型的字典。在此期间,我还生成了 JSON 序列化代码。我已经为非常复杂的模型完成了这项工作,结果证明它可以节省大量时间。

回答by puppybits

Since enums are backed by a standard short you could also not use the NSNumber wrapper and set the property directly as a scalar value. Make sure to set the data type in the core data model as "Integer 32".

由于枚举由标准 short 支持,因此您也不能使用 NSNumber 包装器并将属性直接设置为标量值。确保将核心数据模型中的数据类型设置为“整数 32”。

MyEntity.h

我的实体.h

myEntityInstance.coreDataEnumStorage = kEnumThing;

Elsewhere in code

代码中的其他地方

myEntityInstance.coreDataEnumStorage = [myStringOfAnInteger intValue];

Or parsing from a JSON string or loading from a file

或从 JSON 字符串解析或从文件加载

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

回答by ardochhigh

The code pasted below works for me, and I've added it as full working example. I'd like to hear opinions on this approach, as I plan to used it extensively throughout my apps.

下面粘贴的代码对我有用,我已将其添加为完整的工作示例。我想听听关于这种方法的意见,因为我计划在我的应用程序中广泛使用它。

  • I've left the @dynamic in place, as it is then satisfied by the getter/setter named in the property.

  • As per the answer by iKenndac, I have not overridden the default getter/setter names.

  • I've included some range checking via a NSAssert on the typedef valid values.

  • I've also added a method to obtain a string value for the given typedef.

  • I prefix constants with "c" rather than "k". I know the reasoning behind "k" (math origins, historical), but it feels like I am reading ESL code with it, so I use "c". Just a personal thing.

  • 我已经将@dynamic 留在原地,因为它随后被属性中命名的 getter/setter 满足。

  • 根据 iKenndac 的回答,我没有覆盖默认的 getter/setter 名称。

  • 我已经通过 NSAssert 对 typedef 有效值进行了一些范围检查。

  • 我还添加了一个方法来获取给定 typedef 的字符串值。

  • 我用“c”而不是“k”前缀常量。我知道“k”(数学起源、历史)背后的原因,但感觉就像我在用它阅读 ESL 代码,所以我使用“c”。只是个人的事情。

There is a similar question here: typedef as a Core data type

这里有一个类似的问题:typedef as a Core data type

I'd appreciate any input on this approach.

我很感激对这种方法的任何投入。

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

回答by mgyky

Solution for Auto Generated Classes

自动生成类的解决方案

from Xcode's Code Generator (ios 10 and above)

来自 Xcode 的代码生成器(ios 10 及更高版本)

If you create an Entity named "YourClass", Xcode automatically will choose "Class Definition" as default a Codegen type at "Data Model Inspector". this will generate classes below:

如果您创建一个名为“YourClass”的实体,Xcode 将自动选择“Class Definition”作为“Data Model Inspector”中的默认 Codegen 类型。这将生成以下类:

Swift version:

迅捷版:

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

  #import "YourClass+CoreDataProperties.h"

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

Objective-C version:

Objective-C 版本:

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

We'll choose "Category/Extension" from Codegen option instead of "Class Definition" in Xcode.

我们将从 Codegen 选项中选择“Category/Extension”,而不是 Xcode 中的“Class Definition”。

Now, If we want to add an enum, go and create another extension for your auto-generated class, and add your enum definitions here like below:

现在,如果我们要添加枚举,请为自动生成的类创建另一个扩展,并在此处添加枚举定义,如下所示:

model.yourEnumProperty = (int16_t)YourEnumTypeStarted;

Now, you can create custom accessors if you want to restrict the values to an enum. Please check the accepted answer by question owner. Or you can convert your enums while you set them with explicitly conversion method using the cast operator like below:

现在,如果要将值限制为枚举,则可以创建自定义访问器。请检查问题所有者接受的答案。或者,您可以在使用如下所示的强制转换运算符使用显式转换方法设置枚举时转换枚举:

##代码##

Also check

还要检查

Xcode automatic subclass generation

Xcode 自动子类生成

Xcode now supports automatic generation of NSManagedObject subclasses in the modeling tool. In the entity inspector:

Manual/None is the default, and previous behavior; in this case, you should implement your own subclass or use NSManagedObject. Category/Extension generates a class extension in a file named like ClassName+CoreDataGeneratedProperties. You need to declare/implement the main class (if in Obj-C, via a header the extension can import named ClassName.h). Class Definition generates subclass files named like ClassName+CoreDataClass as well as the files generated for Category/Extension. The generated files are placed in DerivedData and rebuilt on the first build after the model is saved. They are also indexed by Xcode, so command-clicking on references and fast-opening by filename works.

Xcode 现在支持在建模工具中自动生成 NSManagedObject 子类。在实体检查器中:

Manual/None 是默认值和以前的行为;在这种情况下,您应该实现自己的子类或使用 NSManagedObject。类别/扩展在名为 ClassName+CoreDataGeneratedProperties 的文件中生成类扩展。您需要声明/实现主类(如果在 Obj-C 中,扩展可以通过头文件导入名为 ClassName.h)。Class Definition 生成名为 ClassName+CoreDataClass 的子类文件以及为 Category/Extension 生成的文件。生成的文件放置在 DerivedData 中,并在保存模型后在第一次构建时重新构建。它们也由 Xcode 索引,因此命令单击引用并按文件名快速打开工作。