วิธีใดที่ดีที่สุดในการผูกเอนทิตีข้อมูลหลักกับค่า enum เพื่อให้ฉันสามารถกำหนดคุณสมบัติประเภทให้กับเอนทิตีได้ กล่าวอีกนัยหนึ่งฉันมีเอนทิตีที่เรียกItem
ด้วยitemType
คุณสมบัติที่ฉันต้องการผูกมัดกับ enum วิธีที่ดีที่สุดในการดำเนินการนี้คืออะไร
วิธีใดที่ดีที่สุดในการผูกเอนทิตีข้อมูลหลักกับค่า enum เพื่อให้ฉันสามารถกำหนดคุณสมบัติประเภทให้กับเอนทิตีได้ กล่าวอีกนัยหนึ่งฉันมีเอนทิตีที่เรียกItem
ด้วยitemType
คุณสมบัติที่ฉันต้องการผูกมัดกับ enum วิธีที่ดีที่สุดในการดำเนินการนี้คืออะไร
คำตอบ:
คุณจะต้องสร้างตัวเข้าถึงที่กำหนดเองหากคุณต้องการ จำกัด ค่าไว้ที่ enum ก่อนอื่นคุณต้องประกาศ enum ดังนี้:
typedef enum {
kPaymentFrequencyOneOff = 0,
kPaymentFrequencyYearly = 1,
kPaymentFrequencyMonthly = 2,
kPaymentFrequencyWeekly = 3
} PaymentFrequency;
จากนั้นประกาศ getters และ setters สำหรับทรัพย์สินของคุณ เป็นความคิดที่ไม่ดีที่จะลบล้างสิ่งที่มีอยู่เนื่องจากตัวเข้าถึงมาตรฐานคาดหวังอ็อบเจ็กต์ NSNumber มากกว่าประเภทสเกลาร์และคุณจะประสบปัญหาหากมีสิ่งใดในการเชื่อมโยงหรือระบบ KVO พยายามและเข้าถึงค่าของคุณ
- (PaymentFrequency)itemTypeRaw {
return (PaymentFrequency)[[self itemType] intValue];
}
- (void)setItemTypeRaw:(PaymentFrequency)type {
[self setItemType:[NSNumber numberWithInt:type]];
}
สุดท้ายคุณควรใช้+ keyPathsForValuesAffecting<Key>
เพื่อให้คุณได้รับการแจ้งเตือน KVO สำหรับ itemTypeRaw เมื่อ itemType เปลี่ยนไป
+ (NSSet *)keyPathsForValuesAffectingItemTypeRaw {
return [NSSet setWithObject:@"itemType"];
}
int16_t
และคุณตั้งค่าไว้
คุณสามารถทำได้ด้วยวิธีนี้วิธีที่ง่ายกว่า:
typedef enum Types_e : int16_t {
TypeA = 0,
TypeB = 1,
} Types_t;
@property (nonatomic) Types_t itemType;
และในโมเดลของคุณตั้งค่าitemType
เป็นตัวเลข 16 บิต ทุกอย่างเสร็จเรียบร้อย. ไม่จำเป็นต้องใช้รหัสเพิ่มเติม เพียงแค่ใส่ตามปกติของคุณ
@dynamic itemType;
หากคุณใช้ Xcode เพื่อสร้างNSManagedObject
คลาสย่อยของคุณตรวจสอบให้แน่ใจว่าได้เลือกการตั้งค่า " ใช้คุณสมบัติสเกลาร์สำหรับชนิดข้อมูลดั้งเดิม " แล้ว
retain
ที่เกี่ยวข้องกับการจัดการหน่วยความจำไม่ได้ว่าจะได้รับการจัดเก็บไว้ในฐานข้อมูลหรือไม่
แนวทางอื่นที่ฉันกำลังพิจารณาคือไม่ต้องประกาศ enum เลย แต่เป็นการประกาศค่าเป็นวิธีการหมวดหมู่ใน NSNumber แทน
หากคุณกำลังใช้ mogenerator ดูได้ที่นี้: https://github.com/rentzsch/mogenerator/wiki/Using-enums-as-types คุณสามารถเรียกแอตทริบิวต์จำนวนเต็ม 16 itemType
ได้โดยมีattributeValueScalarType
ค่าItem
เป็นข้อมูลผู้ใช้ จากนั้นในข้อมูลผู้ใช้สำหรับเอนทิตีของคุณให้ตั้งadditionalHeaderFileName
เป็นชื่อของส่วนหัวที่Item
enum กำหนดไว้เมื่อสร้างไฟล์ส่วนหัวของคุณ mogenerator จะทำให้คุณสมบัติมีItem
ประเภทโดยอัตโนมัติ
ฉันตั้งค่าประเภทแอตทริบิวต์เป็นจำนวนเต็ม 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
เนื่องจาก enums ได้รับการสนับสนุนโดย short มาตรฐานคุณจึงไม่สามารถใช้ NSNumber wrapper และตั้งค่าคุณสมบัติเป็นค่าสเกลาร์ได้โดยตรง อย่าลืมตั้งค่าประเภทข้อมูลในโมเดลข้อมูลหลักเป็น "จำนวนเต็ม 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];
ฉันทำสิ่งนี้มามากแล้วและพบว่าแบบฟอร์มต่อไปนี้เป็นประโยชน์:
// 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?
ในกรณีนี้ enum ค่อนข้างง่าย:
public enum AccountType: String {
case New = "new"
case Registered = "full"
}
และเรียกมันว่าอวดรู้ แต่ฉันใช้ enums สำหรับชื่อฟิลด์เช่นนี้:
public enum Field:String {
case Account = "account"
}
เนื่องจากสิ่งนี้อาจยุ่งยากสำหรับโมเดลข้อมูลที่ซับซ้อนฉันจึงเขียนตัวสร้างโค้ดที่ใช้ MOM / เอนทิตีเพื่อคายการแมปทั้งหมด อินพุตของฉันกลายเป็นพจนานุกรมจาก Table / Row เป็น Enum type ในขณะที่ฉันอยู่ที่นั่นฉันก็สร้างรหัสซีเรียลไลเซชัน JSON ด้วย ฉันได้ทำสิ่งนี้สำหรับโมเดลที่ซับซ้อนมากและมันกลายเป็นการประหยัดครั้งใหญ่
รหัสที่วางด้านล่างใช้ได้ผลสำหรับฉันและฉันได้เพิ่มเป็นตัวอย่างการทำงานเต็มรูปแบบ ฉันต้องการรับฟังความคิดเห็นเกี่ยวกับแนวทางนี้เนื่องจากฉันวางแผนที่จะใช้มันอย่างกว้างขวางตลอดทั้งแอปของฉัน
ฉันได้ทิ้ง @dynamic ไว้แล้วเนื่องจากเป็นที่พอใจของ getter / setter ที่มีชื่อในคุณสมบัติ
ตามคำตอบของ iKenndac ฉันไม่ได้ลบล้างชื่อ getter / setter เริ่มต้น
ฉันได้รวมการตรวจสอบช่วงผ่าน NSAssert ในค่าที่ถูกต้องของ typedef
ฉันยังได้เพิ่มวิธีการรับค่าสตริงสำหรับ typedef ที่กำหนด
ฉันเติมค่าคงที่ด้วย "c" แทนที่จะเป็น "k" ฉันรู้เหตุผลเบื้องหลัง "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
หากคุณสร้างเอนทิตีชื่อ "YourClass" Xcode จะเลือก "คำจำกัดความของคลาส" โดยอัตโนมัติเป็นค่าเริ่มต้นประเภท Codegen ที่ "Data Model Inspector" สิ่งนี้จะสร้างคลาสด้านล่าง:
// YourClass+CoreDataClass.swift
@objc(YourClass)
public class YourClass: NSManagedObject {
}
// YourClass+CoreDataClass.h
@interface YourClass : NSManagedObject
@end
#import "YourClass+CoreDataProperties.h"
// YourClass+CoreDataClass.m
#import "YourClass+CoreDataClass.h"
@implementation YourClass
@end
เราจะเลือก "หมวดหมู่ / ส่วนขยาย" จากตัวเลือก Codegen แทน "Class Definition" ใน Xcode
ตอนนี้หากเราต้องการเพิ่ม enum ให้ไปและสร้างส่วนขยายอื่นสำหรับคลาสที่สร้างขึ้นโดยอัตโนมัติของคุณและเพิ่มคำจำกัดความ enum ของคุณที่นี่ดังนี้:
// 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
ตอนนี้คุณสามารถสร้างตัวเข้าถึงที่กำหนดเองได้หากคุณต้องการ จำกัด ค่าไว้ที่ enum โปรดตรวจสอบคำตอบที่ได้รับการยอมรับโดยเจ้าของคำถาม หรือคุณสามารถแปลง enums ของคุณในขณะที่คุณตั้งค่าด้วยวิธีการแปลงอย่างชัดเจนโดยใช้ตัวดำเนินการ cast ดังต่อไปนี้:
model.yourEnumProperty = (int16_t)YourEnumTypeStarted;
ขณะนี้ Xcode รองรับคลาสย่อย NSManagedObject ในเครื่องมือการสร้างแบบจำลองโดยอัตโนมัติ ในตัวตรวจสอบเอนทิตี:
คู่มือ / ไม่มีเป็นค่าเริ่มต้นและพฤติกรรมก่อนหน้านี้ ในกรณีนี้คุณควรใช้คลาสย่อยของคุณเองหรือใช้ NSManagedObject ประเภท / ส่วนขยายสร้างส่วนขยายคลาสในไฟล์ชื่อ ClassName + CoreDataGeneratedProperties คุณต้องประกาศ / ใช้งานคลาสหลัก (ถ้าอยู่ใน Obj-C ผ่านส่วนหัวส่วนขยายสามารถนำเข้าชื่อ ClassName.h) นิยามคลาสสร้างไฟล์คลาสย่อยที่ชื่อ ClassName + CoreDataClass เช่นเดียวกับไฟล์ที่สร้างขึ้นสำหรับ Category / Extension ไฟล์ที่สร้างขึ้นจะถูกวางไว้ใน DerivedData และสร้างขึ้นใหม่ในบิลด์แรกหลังจากบันทึกโมเดลแล้ว นอกจากนี้ยังมีการจัดทำดัชนีโดย Xcode ดังนั้นการคลิกคำสั่งที่การอ้างอิงและการเปิดอย่างรวดเร็วตามชื่อไฟล์จึงใช้งานได้
enum
s?