วิธีจัดการกับอินสแตนซ์ NSManagedObject ชั่วคราว


86

ฉันต้องการสร้างNSManagedObjectอินสแตนซ์ทำบางอย่างกับพวกเขาแล้วทิ้งหรือจัดเก็บลงใน sqlite db ปัญหาคือฉันไม่สามารถสร้างอินสแตนซ์ที่NSManagedObjectไม่ได้เชื่อมต่อกับNSManagedObjectContextและนั่นหมายความว่าฉันต้องล้างข้อมูลหลังจากตัดสินใจว่าฉันไม่ต้องการวัตถุบางอย่างในฐานข้อมูลของฉัน

เพื่อจัดการกับมันฉันได้สร้างที่เก็บในหน่วยความจำโดยใช้ผู้ประสานงานคนเดียวกันและฉันวางวัตถุชั่วคราวไว้ที่นั่นโดยใช้assignObject:toPersistentStore.Now ฉันจะแน่ใจได้อย่างไรว่าวัตถุชั่วคราวเหล่านี้จะไม่เข้าถึงข้อมูลซึ่งฉันดึงมาจาก เหมือนกันกับบริบทร้านค้าทั้งสอง? หรือฉันต้องสร้างบริบทแยกต่างหากสำหรับงานดังกล่าว?


UPD:

ตอนนี้ฉันกำลังคิดที่จะสร้างบริบทแยกต่างหากสำหรับที่เก็บในหน่วยความจำ ฉันจะย้ายวัตถุจากบริบทหนึ่งไปยังอีกบริบทได้อย่างไร เพียงแค่ใช้ [บริบท insertObject:]? การตั้งค่านี้จะใช้งานได้หรือไม่ ถ้าฉันแทรกวัตถุหนึ่งจากกราฟของวัตถุกราฟทั้งหมดจะถูกแทรกลงในบริบทด้วยหรือไม่


นี่ควรเป็นคำถามแยกต่างหากเนื่องจากคุณได้ตั้งค่าสถานะคำถามนี้เป็นคำตอบ สร้างคำถามใหม่และอธิบายว่าทำไมคุณรู้สึกว่าคุณจำเป็นต้องมีทั้งที่แยกต่างหากหลักข้อมูลสแต็คเพียงสำหรับการจัดเก็บในหน่วยความจำ เรายินดีที่จะสำรวจคำถามกับคุณ
Marcus S.Zarra

ตอนนี้ส่วน UPD ไม่เกี่ยวข้องเพราะฉันเลือกแนวทางอื่นดูความคิดเห็นล่าสุดของฉันสำหรับคำตอบของคุณ
fspirit

คำตอบ:


146

หมายเหตุ:คำตอบนี้เก่ามาก ดูความคิดเห็นสำหรับประวัติแบบเต็ม หลังจากนั้นคำแนะนำของฉันก็เปลี่ยนไปและฉันไม่แนะนำให้ใช้NSManagedObjectอินสแตนซ์ที่ไม่เกี่ยวข้องอีกต่อไป คำแนะนำปัจจุบันของฉันคือใช้NSManagedObjectContextอินสแตนซ์ลูกชั่วคราว

คำตอบเดิม

วิธีที่ง่ายที่สุดที่จะทำคือการสร้างของคุณกรณีโดยไม่ต้องเกี่ยวข้องNSManagedObjectNSManagedObjectContext

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

จากนั้นเมื่อคุณต้องการบันทึก:

[myMOC insertObject:unassociatedObject];
NSError *error = nil;
if (![myMoc save:&error]) {
  //Respond to the error
}

6
หาก unassociatedObject มีการอ้างถึงอ็อบเจ็กต์อื่น ๆ ที่ไม่เกี่ยวข้องฉันควรแทรกทีละรายการหรือ myMOC ฉลาดพอที่จะรวบรวม refs ทั้งหมดและแทรกด้วยหรือไม่
fspirit

6
มันฉลาดพอที่จะจัดการกับความสัมพันธ์เช่นกัน
Marcus S.Zarra

2
ฉันชอบที่วิธีนี้ช่วยให้คุณปฏิบัติกับ MOs เหมือนออบเจ็กต์ข้อมูลทั่วไปก่อนที่คุณจะตัดสินใจจัดเก็บ แต่ฉันกังวลว่าสัญญา CoreData จะ "รองรับ" อย่างไรและจะป้องกันได้อย่างไรในอนาคต Apple พูดถึงหรือใช้แนวทางนี้ได้ทุกที่หรือไม่? เพราะถ้าไม่เป็นเช่นนั้นการเปิดตัว iOS ในอนาคตอาจเปลี่ยนคุณสมบัติแบบไดนามิกเพื่อขึ้นอยู่กับ MOC และทำลายแนวทางนี้ เอกสารของ Apple ไม่ชัดเจนในเรื่องนี้พวกเขาเน้นถึงความสำคัญของบริบทและตัวเริ่มต้นที่กำหนด แต่มีการกล่าวถึงในเอกสารของ MO กล่าวว่า "ถ้าบริบทไม่ใช่ศูนย์แสดงว่า ... " บอกว่าศูนย์อาจจะโอเค
Rhubarb

41
ฉันใช้วิธีนี้เมื่อไม่นานมานี้ แต่เริ่มเห็นพฤติกรรมแปลก ๆ และเกิดข้อขัดข้องเมื่อฉันแก้ไขวัตถุเหล่านั้นและ / หรือสร้างความสัมพันธ์ให้ก่อนที่จะแทรกลงใน MOC ฉันได้พูดคุยเรื่องนี้กับวิศวกรข้อมูลหลักที่ WWDC และเขากล่าวว่าในขณะที่มี API สำหรับออบเจ็กต์ที่ไม่เกี่ยวข้องอยู่ที่นั่นเขาขอแนะนำอย่างยิ่งให้ไม่ใช้มันเนื่องจาก MOC ต้องอาศัยการแจ้งเตือน KVO ที่ส่งโดยวัตถุ เขาแนะนำให้ใช้ NSObject ปกติสำหรับวัตถุชั่วคราวเนื่องจากปลอดภัยกว่ามาก
Adrian Schönig

7
ดูเหมือนว่าจะใช้งานได้ไม่ดีกับ iOS 8 โดยเฉพาะอย่างยิ่งกับความสัมพันธ์ที่คงอยู่ มีใครยืนยันเรื่องนี้ได้อีกไหม
Janum Trivedi

40

iOS5 เป็นทางเลือกที่ง่ายกว่าสำหรับคำตอบของ Mike Weller ใช้NSManagedObjectContext ลูกแทน ไม่จำเป็นต้องใช้ trampoline ผ่าน NSNotificationCenter

ในการสร้างบริบทลูก:

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = myMangedObjectContext;

จากนั้นสร้างวัตถุของคุณโดยใช้บริบทลูก:

NSManagedObject *o = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:childContext];

การเปลี่ยนแปลงจะใช้เมื่อบันทึกบริบทลูกเท่านั้น ดังนั้นหากต้องการยกเลิกการเปลี่ยนแปลงก็อย่าบันทึก

ยังมีข้อ จำกัด เรื่องความสัมพันธ์ กล่าวคือคุณไม่สามารถสร้างความสัมพันธ์กับวัตถุในบริบทอื่น ๆ หากต้องการหลีกเลี่ยงสิ่งนี้ให้ใช้ objectID เพื่อรับวัตถุจากบริบทลูก เช่น.

NSManagedObjectID *mid = [myManagedObject objectID];
MyManagedObject *mySafeManagedObject = [childContext objectWithID:mid];
object.relationship=mySafeManagedObject;

หมายเหตุการบันทึกบริบทลูกจะใช้การเปลี่ยนแปลงกับบริบทหลัก การบันทึกบริบทหลักยังคงมีการเปลี่ยนแปลง

ดูwwdc 2012 เซสชัน 214สำหรับคำอธิบายทั้งหมด


1
ขอบคุณที่แนะนำสิ่งนี้! ฉันเขียนการสาธิตการทดสอบวิธีนี้เทียบกับการใช้บริบทศูนย์และอย่างน้อยบน OSX สิ่งนี้ใช้ได้ผลในขณะที่การแทรกบริบทศูนย์สูญเสียแอตทริบิวต์เมื่อบันทึก - สาธิตที่github.com/seltzered/CoreDataMagicalRecordTempObjectsDemo
Vivek Gani

ข้อใดอยู่mocในตัวอย่างข้อมูลที่สาม มันchildContextหรือmyMangedObjectContext?
bugloaf

มันคือ childContext
railwayparade

โซลูชันนี้ดีกว่าการมีบริบทศูนย์
จะ Y

ตั้งแต่NSManagedObjectแล้วให้ที่เกี่ยวข้องNSManagedObjectContextคุณสามารถทำให้ทางเลือกของบริบท: แล้วNSManagedObject* objectRelatedContextually = [objectWithRelationship.managedObjectContext objectWithID:objectRelated.objectID]; objectWithRelationship.relationship = objectRelatedContextually;
Gary

9

วิธีที่ถูกต้องในการบรรลุสิ่งนี้คือการใช้บริบทอ็อบเจ็กต์ที่มีการจัดการใหม่ คุณสร้างบริบทอ็อบเจ็กต์ที่มีการจัดการด้วยที่เก็บถาวรเดียวกัน:

NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
[tempContext setPersistentStore:[originalContext persistentStore]];

จากนั้นคุณเพิ่มวัตถุใหม่กลายพันธุ์เป็นต้น

เมื่อถึงเวลาบันทึกคุณต้องเรียกใช้ [tempContext save: ... ] บน tempContext และจัดการการแจ้งเตือนเพื่อรวมเข้ากับบริบทเดิมของคุณ หากต้องการทิ้งวัตถุเพียงแค่ปล่อยบริบทชั่วคราวนี้และลืมมัน

ดังนั้นเมื่อคุณบันทึกบริบทชั่วคราวการเปลี่ยนแปลงจะยังคงอยู่ในร้านค้าและคุณเพียงแค่นำการเปลี่ยนแปลงเหล่านั้นกลับเข้าสู่บริบทหลักของคุณ:

/* Called when the temp context is saved */
- (void)tempContextSaved:(NSNotification *)notification {
    /* Merge the changes into the original managed object context */
    [originalContext mergeChangesFromContextDidSaveNotification:notification];
}

// Here's where we do the save itself

// Add the notification handler
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(tempContextSaved:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:tempContext];

// Save
[tempContext save:NULL];
// Remove the handler again
[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSManagedObjectContextDidSaveNotification
                                              object:tempContext];

นี่เป็นวิธีที่คุณควรจัดการกับการดำเนินการกับข้อมูลหลักแบบมัลติเธรด หนึ่งบริบทต่อเธรด

หากคุณต้องการเข้าถึงอ็อบเจ็กต์ที่มีอยู่จากบริบทชั่วคราวนี้ (เพื่อเพิ่มความสัมพันธ์ ฯลฯ ) คุณต้องใช้ ID ของอ็อบเจ็กต์เพื่อรับอินสแตนซ์ใหม่ดังนี้:

NSManagedObject *objectInOriginalContext = ...;
NSManagedObject *objectInTemporaryContext = [tempContext objectWithID:[objectInOriginalContext objectID]];

หากคุณพยายามใช้NSManagedObjectในบริบทที่ไม่ถูกต้องคุณจะได้รับข้อยกเว้นขณะบันทึก


การสร้างบริบทที่สองสำหรับสิ่งนี้เป็นการสิ้นเปลืองมากเนื่องจากการยืนขึ้นมีNSManagedObjectContextราคาแพงทั้งในหน่วยความจำและ CPU ฉันรู้ว่านี่เป็นตัวอย่างของ Apple แต่เดิมได้อัปเดตและแก้ไขตัวอย่างเหล่านั้นแล้ว
Marcus S.Zarra

2
Apple ยังคงใช้เทคนิคนี้ (การสร้างบริบทอ็อบเจ็กต์ที่มีการจัดการที่สอง) สำหรับโค้ดตัวอย่าง CoreDataBooks
เนวานคิง

1
หมายเหตุ Apple ได้อัปเดต CoreDataBooks ซึ่งยังคงใช้สองบริบท แต่ตอนนี้บริบทที่ 2 เป็นลูกของเนื้อหาแรก เทคนิคนี้จะกล่าวถึง (และแนะนำ) ในการนำเสนอ WWDC 2011 303 (มีอะไรใหม่ใน Core Data ใน iOS) และมีการกล่าวถึงที่นี่ (มีโค้ดที่ง่ายกว่ามากสำหรับการรวมการเปลี่ยนแปลงขึ้นไป) stackoverflow.com/questions/9791469/…
Rhubarb

4
"การสร้างบริบทที่สองเพื่อสิ่งนี้จะสิ้นเปลืองมากเนื่องจากการยืนขึ้น NSManagedObjectContext นั้นมีราคาแพงทั้งในหน่วยความจำและ CPU" . ไม่มันไม่ใช่. การพึ่งพาของผู้ประสานงานร้านค้าถาวร (โมเดลอ็อบเจ็กต์ที่มีการจัดการและร้านค้าคอนกรีต) ไม่ใช่บริบท บริบทมีน้ำหนักเบา
ดับ

3
@quellish เห็นด้วย Apple ได้กล่าวไว้ในการพูดคุยเกี่ยวกับประสิทธิภาพข้อมูลหลักล่าสุดที่ WWDC ว่าการสร้างบริบทมีน้ำหนักเบามาก
Jesse

9

การสร้างอ็อบเจกต์ชั่วคราวจาก nil context ทำงานได้ดีจนกว่าคุณจะพยายามมีความสัมพันธ์กับอ็อบเจ็กต์ที่มีบริบท! = nil!

ให้แน่ใจว่าคุณโอเคกับสิ่งนั้น


ฉันไม่โอเคกับสิ่งนั้น
Charlie

8

สิ่งที่คุณอธิบายคือสิ่งที่มีNSManagedObjectContextไว้สำหรับ

จากCore Data Programming Guide: Core Data Basics

คุณสามารถคิดว่าบริบทของวัตถุที่มีการจัดการเป็นแผ่นรอยขีดข่วนอัจฉริยะ เมื่อคุณดึงวัตถุจากที่เก็บถาวรคุณจะนำสำเนาชั่วคราวมาไว้บนแผ่นขูดซึ่งสร้างเป็นกราฟวัตถุ (หรือชุดของกราฟวัตถุ) จากนั้นคุณสามารถปรับเปลี่ยนวัตถุเหล่านั้นได้ตามต้องการ อย่างไรก็ตามเว้นแต่คุณจะบันทึกการเปลี่ยนแปลงเหล่านั้นจริง ๆ ที่เก็บถาวรจะยังคงไม่เปลี่ยนแปลง

และคู่มือการเขียนโปรแกรมข้อมูลหลัก: การตรวจสอบวัตถุที่มีการจัดการ

นอกจากนี้ยังสนับสนุนแนวคิดของบริบทอ็อบเจ็กต์ที่มีการจัดการซึ่งเป็นตัวแทนของ "แผ่นรอยขีดข่วน" โดยทั่วไปคุณสามารถนำอ็อบเจ็กต์ที่มีการจัดการมาไว้บนแผ่นรอยขีดข่วนและแก้ไขได้ตามที่คุณต้องการก่อนที่จะดำเนินการเปลี่ยนแปลงหรือยกเลิก

NSManagedObjectContexts ถูกออกแบบมาให้มีน้ำหนักเบา คุณสามารถสร้างและทิ้งได้ตามต้องการ - เป็นผู้ประสานงานร้านค้าถาวรและเป็นที่พึ่งพาที่ "หนัก" ผู้ประสานงานร้านค้าถาวรคนเดียวสามารถมีบริบทหลายอย่างที่เกี่ยวข้อง ภายใต้รูปแบบการ จำกัด เธรดที่เก่ากว่าและล้าสมัยสิ่งนี้จะหมายถึงการตั้งค่าผู้ประสานงานการจัดเก็บถาวรคนเดียวกันในแต่ละบริบท วันนี้จะหมายถึงการเชื่อมต่อบริบทที่ซ้อนกันกับบริบทรากที่เชื่อมโยงกับผู้ประสานงานร้านค้าถาวร

สร้างบริบทสร้างและแก้ไขอ็อบเจ็กต์ที่มีการจัดการภายในบริบทนั้น หากคุณต้องการคงอยู่และสื่อสารการเปลี่ยนแปลงเหล่านั้นให้บันทึกบริบท มิฉะนั้นทิ้งมัน

การพยายามสร้างอ็อบเจ็กต์ที่มีการจัดการโดยไม่ขึ้นNSManagedObjectContextอยู่กับกำลังขอปัญหา โปรดจำไว้ว่าข้อมูลหลักในท้ายที่สุดคือกลไกการติดตามการเปลี่ยนแปลงสำหรับกราฟออบเจ็กต์ ด้วยเหตุนี้วัตถุที่มีการจัดการที่เป็นจริงส่วนหนึ่งของบริบทการจัดการวัตถุ บริบทจะสังเกตวงจรชีวิตของพวกเขาและหากไม่มีบริบทฟังก์ชันอ็อบเจ็กต์ที่มีการจัดการทั้งหมดจะทำงานได้อย่างถูกต้อง


6

ขึ้นอยู่กับการใช้วัตถุชั่วคราวของคุณมีข้อควรระวังบางประการสำหรับคำแนะนำข้างต้น กรณีการใช้งานของฉันคือฉันต้องการสร้างวัตถุชั่วคราวและเชื่อมโยงกับมุมมอง เมื่อผู้ใช้เลือกที่จะบันทึกวัตถุนี้ฉันต้องการตั้งค่าความสัมพันธ์กับวัตถุที่มีอยู่และบันทึก ฉันต้องการทำสิ่งนี้เพื่อหลีกเลี่ยงการสร้างวัตถุชั่วคราวเพื่อเก็บค่าเหล่านั้น (ใช่ฉันสามารถรอจนกว่าผู้ใช้จะบันทึกแล้วจึงคว้าเนื้อหามุมมอง แต่ฉันกำลังใส่มุมมองเหล่านี้ไว้ในตารางและตรรกะในการทำสิ่งนี้ก็ดูสง่างามน้อยลง)

ตัวเลือกสำหรับวัตถุชั่วคราว ได้แก่ :

1) (ต้องการ) สร้างวัตถุชั่วคราวในบริบทลูก สิ่งนี้ใช้ไม่ได้เพราะฉันผูกวัตถุกับ UI และฉันไม่สามารถรับประกันได้ว่าจะเรียกตัวเข้าถึงวัตถุในบริบทลูก (ฉันไม่พบเอกสารที่ระบุเป็นอย่างอื่นดังนั้นฉันจึงต้องถือว่า)

2) สร้างวัตถุชั่วคราวโดยไม่มีบริบทวัตถุ วิธีนี้ไม่ได้ผลและส่งผลให้ข้อมูลสูญหาย / เสียหาย

วิธีแก้ไขของฉัน: ฉันแก้ไขสิ่งนี้โดยการสร้างวัตถุชั่วคราวที่มีบริบทวัตถุศูนย์ แต่เมื่อฉันบันทึกวัตถุแทนที่จะแทรกเป็น # 2 ฉันคัดลอกแอตทริบิวต์ทั้งหมดลงในวัตถุใหม่ที่ฉันสร้างในบริบทหลัก ฉันสร้างวิธีการสนับสนุนในคลาสย่อย NSManagedObject ของฉันที่เรียกว่า cloneInto: ซึ่งช่วยให้ฉันคัดลอกแอตทริบิวต์และความสัมพันธ์สำหรับวัตถุใด ๆ ได้อย่างง่ายดาย


นั่นคือสิ่งที่ฉันกำลังมองหา แต่ข้อสงสัยของฉันคือคุณจะจัดการกับคุณลักษณะความสัมพันธ์อย่างไร?
มานี

1

สำหรับฉันคำตอบของมาร์คัสไม่ได้ผล นี่คือสิ่งที่ใช้ได้ผลสำหรับฉัน:

NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

จากนั้นถ้าฉันตัดสินใจที่จะบันทึก:

[myMOC insertObject:unassociatedObjet];
NSError *error = nil;
[myMoc save:&error];
//Check the error!

เราต้องไม่ลืมที่จะปล่อยมันด้วย

[unassociatedObject release]

1

ฉันเขียนคำตอบนี้ใหม่สำหรับ Swift เป็นคำถามที่คล้ายกันทั้งหมดสำหรับการเปลี่ยนเส้นทางไปยังคำถามนี้อย่างรวดเร็ว

คุณสามารถประกาศอ็อบเจ็กต์โดยไม่มี ManagedContext โดยใช้รหัสต่อไปนี้

let entity = NSEntityDescription.entity(forEntityName: "EntityName", in: myContext)
let unassociatedObject = NSManagedObject.init(entity: entity!, insertInto: nil)

ในภายหลังหากต้องการบันทึกวัตถุคุณสามารถแทรกลงในบริบทและบันทึกได้

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.