Làm thế nào để đối phó với các phiên bản NSManagedObject tạm thời?


86

Tôi cần tạo các NSManagedObjectphiên bản, thực hiện một số nội dung với chúng và sau đó chuyển chúng vào thùng rác hoặc lưu trữ vào sqlite db. Vấn đề là, tôi không thể tạo các trường hợp NSManagedObjectkhông được kết nối NSManagedObjectContextvà điều này có nghĩa là tôi phải xóa bằng cách nào đó sau khi tôi quyết định rằng tôi không cần một số đối tượng trong db của mình.

Để đối phó với nó, tôi đã tạo một kho lưu trữ trong bộ nhớ bằng cách sử dụng cùng một bộ điều phối và tôi đang đặt các đối tượng tạm thời ở đó bằng cách sử dụng assignObject:toPersistentStore.Now, làm cách nào để đảm bảo rằng các đối tượng tạm thời này không truy cập vào dữ liệu mà tôi lấy từ chung cho cả bối cảnh cửa hàng? Hay tôi phải tạo các ngữ cảnh riêng biệt cho một nhiệm vụ như vậy?


CẬP NHẬT:

Bây giờ tôi đang nghĩ đến việc tạo ngữ cảnh riêng biệt cho kho lưu trữ trong bộ nhớ. Làm cách nào để di chuyển các đối tượng từ ngữ cảnh này sang ngữ cảnh khác? Chỉ sử dụng [context insertObject:]? Nó sẽ hoạt động OK trong thiết lập này? Nếu tôi chèn một đối tượng từ biểu đồ các đối tượng, thì toàn bộ biểu đồ có được chèn vào ngữ cảnh không?


Đây phải là một câu hỏi riêng biệt vì bạn đã gắn cờ câu hỏi này là đã được trả lời. Tạo một câu hỏi mới và giải thích TẠI SAO bạn cảm thấy mình cần toàn bộ ngăn xếp Dữ liệu cốt lõi riêng biệt CHỈ cho một kho lưu trữ trong bộ nhớ. Tôi sẽ rất vui khi khám phá câu hỏi với bạn.
Marcus S. Zarra

Phần UPD bây giờ không có liên quan, vì tôi đã chọn cách tiếp cận khác, hãy xem nhận xét cuối cùng của tôi cho câu trả lời của bạn.
fspirit,

Câu trả lời:


146

LƯU Ý: Câu trả lời này rất cũ. Xem bình luận để biết lịch sử đầy đủ. Đề xuất của tôi đã thay đổi và tôi không còn khuyên bạn nên sử dụng các NSManagedObjectphiên bản chưa liên kết nữa. Khuyến nghị hiện tại của tôi là sử dụng các NSManagedObjectContextphiên bản con tạm thời .

Câu trả lời gốc

Cách dễ nhất để làm điều này là tạo các NSManagedObjectphiên bản của bạn mà không có liên kết NSManagedObjectContext.

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

Sau đó, khi bạn muốn lưu nó:

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

6
Nếu unassociatedObject có các tham chiếu đến các đối tượng chưa được liên kết khác, tôi nên chèn chúng từng cái một hay myMOC đủ thông minh để thu thập tất cả các tham chiếu và cũng chèn chúng?
fspirit

6
Nó đủ thông minh để xử lý các mối quan hệ.
Marcus S. Zarra

2
Tôi thích cách tiếp cận này cho phép bạn xử lý các MO như các đối tượng dữ liệu thông thường trước khi bạn quyết định lưu trữ chúng nhưng lo lắng về việc hợp đồng CoreData được "hỗ trợ" như thế nào và do đó nó sẽ hoạt động như thế nào trong tương lai. Apple có đề cập hoặc sử dụng cách tiếp cận này ở bất cứ đâu không? Bởi vì nếu không, bản phát hành iOS trong tương lai có thể thay đổi các thuộc tính động để phụ thuộc vào MOC và phá vỡ cách tiếp cận này. Các tài liệu apple không rõ ràng về điều này: họ nhấn mạnh tầm quan trọng của ngữ cảnh và trình khởi tạo được chỉ định, nhưng có một đề cập trong tài liệu MO nói rằng "nếu ngữ cảnh không phải là nil, thì ..." cho thấy rằng nil có thể ổn
Rhubarb,

41
Tôi đã sử dụng cách tiếp cận này một thời gian trước nhưng bắt đầu thấy hành vi lạ và sự cố khi tôi sửa đổi các đối tượng đó và / hoặc tạo mối quan hệ cho chúng trước khi chèn chúng vào MOC. Tôi đã nói chuyện này với một kỹ sư Dữ liệu cốt lõi tại WWDC và anh ấy nói rằng mặc dù API cho các đối tượng không liên kết ở đó nhưng anh ấy đặc biệt khuyên không nên sử dụng nó như một MOC phụ thuộc nhiều vào các thông báo KVO do các đối tượng của nó gửi. Ông đề xuất sử dụng NSObject thông thường cho các đối tượng tạm thời vì điều đó an toàn hơn nhiều.
Adrian Schönig

7
Điều này dường như không hoạt động tốt với iOS 8, đặc biệt là với các mối quan hệ lâu dài. bất cứ ai khác có thể xác nhận điều này?
Janum Trivedi

40

iOS5 cung cấp một giải pháp thay thế đơn giản hơn cho câu trả lời của Mike Weller. Thay vào đó, hãy sử dụng NSManagedObjectContext con . Nó loại bỏ nhu cầu lướt ván qua NSNotificationCenter

Để tạo ngữ cảnh con:

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

Sau đó, tạo các đối tượng của bạn bằng cách sử dụng ngữ cảnh con:

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

Các thay đổi chỉ được áp dụng khi ngữ cảnh con được lưu. Vì vậy, để loại bỏ các thay đổi chỉ cần không lưu.

Vẫn có một giới hạn trong các mối quan hệ. tức là Bạn không thể tạo mối quan hệ với các đối tượng trong các ngữ cảnh khác. Để giải quyết vấn đề này, hãy sử dụng objectID's, để lấy đối tượng từ ngữ cảnh con. ví dụ.

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

Lưu ý, lưu ngữ cảnh con sẽ áp dụng các thay đổi đối với ngữ cảnh mẹ. Lưu ngữ cảnh gốc vẫn giữ nguyên những thay đổi.

Xem wwdc 2012 phiên 214 để được giải thích đầy đủ.


1
Cảm ơn đã gợi ý điều này! Tôi đã viết một bản demo thử nghiệm phương pháp này so với sử dụng một bối cảnh nil và ít nhất trên OSX, điều này làm việc trong khi chèn một bối cảnh nil mất thuộc tính của nó khi lưu - demo tại github.com/seltzered/CoreDataMagicalRecordTempObjectsDemo
Vivek Gani

Cái nào moctrong đoạn mã thứ ba? Là nó childContexthay myMangedObjectContext?
bugloaf

Đây là childContext
railwayparade

giải pháp này tốt hơn là có ngữ cảnh nil.
Will Y

NSManagedObjectđã cung cấp nội dung có liên quan NSManagedObjectContext, bạn có thể tự động lựa chọn ngữ cảnh: NSManagedObject* objectRelatedContextually = [objectWithRelationship.managedObjectContext objectWithID:objectRelated.objectID];và sau đó objectWithRelationship.relationship = objectRelatedContextually;.
Gary

9

Cách chính xác để đạt được loại điều này là với một ngữ cảnh đối tượng được quản lý mới. Bạn tạo bối cảnh đối tượng được quản lý với cùng một cửa hàng liên tục:

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

Sau đó, bạn thêm các đối tượng mới, thay đổi chúng, v.v.

Khi đến lúc lưu, bạn cần gọi [tempContext save: ...] trên tempContext và xử lý thông báo lưu để hợp nhất thông báo đó vào ngữ cảnh ban đầu của bạn. Để loại bỏ các đối tượng, chỉ cần giải phóng ngữ cảnh tạm thời này và quên nó đi.

Vì vậy, khi bạn lưu ngữ cảnh tạm thời, các thay đổi vẫn được lưu giữ trong cửa hàng và bạn chỉ cần đưa những thay đổi đó trở lại ngữ cảnh chính của mình:

/* 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];

Đây cũng là cách bạn nên xử lý các hoạt động dữ liệu lõi đa luồng. Một ngữ cảnh cho mỗi chủ đề.

Nếu bạn cần truy cập các đối tượng hiện có từ ngữ cảnh tạm thời này (để thêm quan hệ, v.v.) thì bạn cần sử dụng ID của đối tượng để nhận một phiên bản mới như sau:

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

Nếu bạn cố gắng sử dụng NSManagedObjectsai ngữ cảnh, bạn sẽ nhận được ngoại lệ khi lưu.


Tạo bối cảnh thứ hai chỉ cho điều này là rất lãng phí vì việc dựng một bối cảnh rất NSManagedObjectContexttốn kém về cả bộ nhớ và CPU. Tôi nhận ra điều này ban đầu có trong một số ví dụ của Apple, nhưng họ đã cập nhật và sửa chữa những ví dụ đó.
Marcus S. Zarra

2
Apple vẫn đang sử dụng kỹ thuật này (tạo bối cảnh đối tượng được quản lý thứ hai) cho mã ví dụ CoreDataBooks.
nevan king

1
Lưu ý rằng Apple đã cập nhật CoreDataBooks, thực sự nó vẫn sử dụng hai ngữ cảnh, nhưng bây giờ ngữ cảnh thứ hai là con của bối cảnh đầu tiên. Kỹ thuật này được thảo luận (và được khuyến nghị) trong bản trình bày 303 của WWDC 2011 (có gì mới trong Dữ liệu cốt lõi trong iOS) và được đề cập ở đây (với rất nhiều, NHIỀU, mã đơn giản hơn để hợp nhất các thay đổi trở lên) stackoverflow.com/questions/9791469/…
Rhubarb

4
"Việc tạo bối cảnh thứ hai chỉ cho việc này là rất lãng phí vì việc xây dựng một NSManagedObjectContext tốn kém cả bộ nhớ và CPU." . Không, không phải. Sự phụ thuộc của điều phối viên cửa hàng liên tục (mô hình đối tượng được quản lý và cửa hàng cụ thể), không phải là bối cảnh. Các khung cảnh có dung lượng nhẹ.
dập tắt

3
@quellish Đồng ý. Apple đã tuyên bố trong các cuộc nói chuyện về hiệu suất dữ liệu cốt lõi gần đây của họ tại WWDC rằng việc tạo bối cảnh là rất nhẹ.
Jesse

9

Tạo các đối tượng tạm thời từ ngữ cảnh nil hoạt động tốt cho đến khi bạn thực sự cố gắng có mối quan hệ với một đối tượng có ngữ cảnh! = Nil!

đảm bảo rằng bạn ổn với điều đó.


Tôi không đồng ý với điều đó
Charlie

8

Những gì bạn đang mô tả là chính xác những gì an NSManagedObjectContextlà cho.

Từ Hướng dẫn lập trình dữ liệu cốt lõi: Khái niệm cơ bản về dữ liệu cốt lõi

Bạn có thể coi ngữ cảnh đối tượng được quản lý như một bảng ghi chép thông minh. Khi bạn tìm nạp các đối tượng từ một kho lưu trữ liên tục, bạn đưa các bản sao tạm thời lên bảng nháp nơi chúng tạo thành một biểu đồ đối tượng (hoặc một bộ sưu tập các biểu đồ đối tượng). Sau đó, bạn có thể sửa đổi các đối tượng đó theo cách bạn muốn. Tuy nhiên, trừ khi bạn thực sự lưu những thay đổi đó, kho lưu trữ liên tục vẫn không thay đổi.

Hướng dẫn lập trình dữ liệu cốt lõi: Xác thực đối tượng được quản lý

Điều này cũng làm cơ sở cho ý tưởng về bối cảnh đối tượng được quản lý đại diện cho "bảng đầu" — nói chung, bạn có thể đưa các đối tượng được quản lý lên bảng đầu và chỉnh sửa chúng theo cách bạn muốn trước khi thực hiện các thay đổi hoặc loại bỏ chúng.

NSManagedObjectContexts được thiết kế để nhẹ. Bạn có thể tạo và loại bỏ chúng theo ý muốn - đó là điều phối viên liên tục của các cửa hàng và đó là các yếu tố phụ thuộc rất "nặng nề". Một điều phối viên cửa hàng liên tục duy nhất có thể có nhiều ngữ cảnh liên kết với nó. Theo mô hình hạn chế luồng cũ hơn, lỗi thời, điều này có nghĩa là thiết lập cùng một trình điều phối cửa hàng liên tục trên mỗi ngữ cảnh. Ngày nay, nó có nghĩa là kết nối các ngữ cảnh lồng nhau với một ngữ cảnh gốc được liên kết với trình điều phối cửa hàng liên tục.

Tạo ngữ cảnh, tạo và sửa đổi các đối tượng được quản lý trong ngữ cảnh đó. Nếu bạn muốn duy trì chúng và truyền đạt những thay đổi đó, hãy lưu ngữ cảnh. Nếu không, hãy loại bỏ nó.

Cố gắng tạo các đối tượng được quản lý độc lập với an NSManagedObjectContextlà một vấn đề. Hãy nhớ rằng Dữ liệu cốt lõi cuối cùng là một cơ chế theo dõi thay đổi cho một biểu đồ đối tượng. Do đó, các đối tượng được quản lý thực sự là một phần của ngữ cảnh đối tượng được quản lý . Bối cảnh quan sát vòng đời của chúngkhông có ngữ cảnh thì không phải tất cả chức năng của đối tượng được quản lý sẽ hoạt động chính xác.


6

Tùy thuộc vào việc sử dụng đối tượng tạm thời của bạn, có một số lưu ý đối với các khuyến nghị trên. Trường hợp sử dụng của tôi là tôi muốn tạo một đối tượng tạm thời và ràng buộc nó với các khung nhìn. Khi người dùng chọn lưu đối tượng này, tôi muốn thiết lập mối quan hệ với (các) đối tượng hiện có và lưu. Tôi muốn làm điều này để tránh tạo một đối tượng tạm thời để giữ các giá trị đó. (Có, tôi chỉ có thể đợi cho đến khi người dùng lưu và sau đó lấy nội dung chế độ xem nhưng tôi đang đặt các chế độ xem này bên trong bảng và logic để làm điều này kém thanh lịch hơn.)

Các tùy chọn cho các đối tượng tạm thời là:

1) (Ưu tiên) Tạo đối tượng tạm thời trong ngữ cảnh con. Điều này sẽ không hoạt động vì tôi đang ràng buộc đối tượng với giao diện người dùng và tôi không thể đảm bảo rằng các trình truy cập đối tượng được gọi trên ngữ cảnh con. (Tôi không tìm thấy tài liệu nào nói khác nên tôi phải giả định.)

2) Tạo đối tượng tạm thời với ngữ cảnh đối tượng nil. Điều này không hoạt động và dẫn đến mất / hỏng dữ liệu.

Giải pháp của tôi: Tôi đã giải quyết vấn đề này bằng cách tạo đối tượng tạm thời với ngữ cảnh đối tượng nil nhưng khi tôi lưu đối tượng, thay vì chèn nó dưới dạng # 2, tôi sao chép tất cả các thuộc tính của nó vào một đối tượng mới mà tôi tạo trong ngữ cảnh chính. Tôi đã tạo một phương thức hỗ trợ trong lớp con NSManagedObject của mình được gọi là cloneInto: cho phép tôi sao chép các thuộc tính và mối quan hệ dễ dàng cho bất kỳ đối tượng nào.


Đó là những gì tôi đang tìm kiếm. Nhưng nghi ngờ của tôi là bạn sẽ xử lý các thuộc tính mối quan hệ như thế nào?
Mani,

1

Đối với tôi câu trả lời của Marcus không hiệu quả. Đây là những gì đã làm việc cho tôi:

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

sau đó, nếu tôi quyết định lưu nó:

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

Chúng tôi cũng không được quên phát hành nó

[unassociatedObject release]

1

Tôi đang viết lại câu trả lời này cho Swift như tất cả các câu hỏi tương tự để chuyển hướng nhanh đến câu hỏi này.

Bạn có thể khai báo đối tượng mà không có ManagedContext nào bằng mã sau.

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

Sau đó, để lưu đối tượng, bạn có thể chèn nó vào ngữ cảnh và lưu nó.

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
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.