Câu hỏi : Làm cách nào để đưa ngữ cảnh con của tôi thấy các thay đổi vẫn tồn tại trên ngữ cảnh mẹ để chúng kích hoạt NSFetchedResultsController của tôi cập nhật giao diện người dùng?
Đây là thiết lập:
Bạn có một ứng dụng tải xuống và thêm nhiều dữ liệu XML (khoảng 2 triệu bản ghi, mỗi bản ghi có kích thước gần bằng một đoạn văn bản thông thường) Tệp .sqlite có kích thước khoảng 500 MB. Việc thêm nội dung này vào Dữ liệu cốt lõi mất thời gian, nhưng bạn muốn người dùng có thể sử dụng ứng dụng trong khi dữ liệu tải vào kho dữ liệu tăng dần. Nó phải vô hình và không thể nhận thấy đối với người dùng rằng một lượng lớn dữ liệu đang được di chuyển xung quanh, do đó, không bị treo, không bị giật: cuộn như bơ. Tuy nhiên, ứng dụng hữu ích hơn, càng nhiều dữ liệu được thêm vào, vì vậy chúng tôi không thể đợi mãi cho dữ liệu được thêm vào kho dữ liệu Core. Trong mã, điều này có nghĩa là tôi thực sự muốn tránh mã như thế này trong mã nhập:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
Ứng dụng chỉ dành cho iOS 5 nên thiết bị chậm nhất cần hỗ trợ là iPhone 3GS.
Dưới đây là các tài nguyên tôi đã sử dụng cho đến nay để phát triển giải pháp hiện tại của mình:
Hướng dẫn lập trình dữ liệu cốt lõi của Apple: Nhập dữ liệu hiệu quả
- Sử dụng Autorelease Pools để giảm bộ nhớ
- Các mối quan hệ Chi phí. Nhập phẳng, sau đó vá các mối quan hệ ở cuối
- Đừng truy vấn nếu bạn có thể giúp nó, nó làm chậm mọi thứ theo cách O (n ^ 2)
- Nhập hàng loạt: lưu, đặt lại, thoát và lặp lại
- Tắt Trình quản lý hoàn tác khi nhập
iDeveloper TV - Hiệu suất dữ liệu cốt lõi
- Sử dụng 3 bối cảnh: Loại ngữ cảnh chính, chính và giam giữ
iDeveloper TV - Cập nhật dữ liệu cốt lõi cho Mac, iPhone và iPad
- Chạy lưu trên các hàng đợi khác với performanceBlock giúp mọi thứ trở nên nhanh chóng.
- Mã hóa làm chậm mọi thứ, hãy tắt nó đi nếu bạn có thể.
Nhập và hiển thị các tập dữ liệu lớn trong dữ liệu cốt lõi của Marcus Zarra
- Bạn có thể làm chậm quá trình nhập bằng cách dành thời gian cho vòng chạy hiện tại để mọi thứ trở nên suôn sẻ cho người dùng.
- Mã mẫu chứng minh rằng có thể thực hiện việc nhập lớn và giữ cho giao diện người dùng đáp ứng nhanh, nhưng không nhanh như với 3 ngữ cảnh và lưu không đồng bộ vào đĩa.
Giải pháp hiện tại của tôi
Tôi đã có 3 phiên bản NSManagedObjectContext:
masterManagedObjectContext - Đây là ngữ cảnh có NSPersistingStoreCoordinator và chịu trách nhiệm lưu vào đĩa. Tôi làm điều này để các lưu của tôi có thể không đồng bộ và do đó rất nhanh. Tôi tạo nó khi khởi chạy như thế này:
masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
mainManagedObjectContext - Đây là ngữ cảnh mà giao diện người dùng sử dụng ở mọi nơi. Nó là con của masterManagedObjectContext. Tôi tạo nó như thế này:
mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[mainManagedObjectContext setUndoManager:nil];
[mainManagedObjectContext setParentContext:masterManagedObjectContext];
backgroundContext - Ngữ cảnh này được tạo trong lớp con NSOperation của tôi, lớp này chịu trách nhiệm nhập dữ liệu XML vào Dữ liệu cốt lõi. Tôi tạo nó trong phương thức chính của hoạt động và liên kết nó với ngữ cảnh chính ở đó.
backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[backgroundContext setUndoManager:nil];
[backgroundContext setParentContext:masterManagedObjectContext];
Điều này thực sự hoạt động rất, RẤT nhanh. Chỉ bằng cách thực hiện 3 thiết lập ngữ cảnh này, tôi đã có thể cải thiện tốc độ nhập của mình hơn 10 lần! Thành thật mà nói, điều này thật khó tin. (Thiết kế cơ bản này phải là một phần của mẫu Dữ liệu cốt lõi tiêu chuẩn ...)
Trong quá trình nhập tôi lưu 2 cách khác nhau. Mỗi 1000 mục tôi lưu trên bối cảnh nền:
BOOL saveSuccess = [backgroundContext save:&error];
Sau đó, vào cuối quá trình nhập, tôi lưu trên ngữ cảnh chính / cha, bề ngoài, đẩy các sửa đổi ra các ngữ cảnh con khác bao gồm ngữ cảnh chính:
[masterManagedObjectContext performBlock:^{
NSError *parentContextError = nil;
BOOL parentContextSaveSuccess = [masterManagedObjectContext save:&parentContextError];
}];
Vấn đề : Vấn đề là giao diện người dùng của tôi sẽ không cập nhật cho đến khi tôi tải lại chế độ xem.
Tôi có một UIViewController đơn giản với một UITableView đang được cung cấp dữ liệu bằng NSFetchedResultsController. Khi quá trình Nhập hoàn tất, NSFetchedResultsController không thấy có thay đổi nào từ ngữ cảnh chính / chính và do đó, giao diện người dùng không tự động cập nhật như tôi vẫn thấy. Nếu tôi bật UIViewController ra khỏi ngăn xếp và tải lại thì tất cả dữ liệu ở đó.
Câu hỏi : Làm cách nào để đưa ngữ cảnh con của tôi thấy các thay đổi vẫn tồn tại trên ngữ cảnh mẹ để chúng kích hoạt NSFetchedResultsController của tôi cập nhật giao diện người dùng?
Tôi đã thử những cách sau chỉ làm treo ứng dụng:
- (void)saveMasterContext {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
NSError *error = nil;
BOOL saveSuccess = [masterManagedObjectContext save:&error];
[notificationCenter removeObserver:self name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
}
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == mainManagedObjectContext) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}