2012-07-04 25 views
7

Tôi đang xây dựng ứng dụng iOS đầu tiên của mình, lý thuyết nên khá đơn giản nhưng tôi gặp khó khăn khi làm cho nó đủ khả năng chống đạn để tôi cảm thấy tự tin khi gửi ứng dụng vào App Store.Sử dụng dữ liệu cốt lõi đồng thời và đáng tin cậy

Một thời gian ngắn, màn hình chính có chế độ xem bảng, khi chọn một hàng, nó sẽ chuyển sang chế độ xem bảng khác hiển thị thông tin liên quan đến hàng đã chọn theo kiểu chi tiết chính. Dữ liệu cơ bản được lấy ra dưới dạng dữ liệu JSON từ một dịch vụ web một lần một ngày và sau đó được lưu trữ trong một kho lưu trữ dữ liệu lõi. Dữ liệu trước đó cho đến ngày đó sẽ bị xóa để ngăn chặn tệp cơ sở dữ liệu SQLite phát triển vô thời hạn. Tất cả các hoạt động liên tục dữ liệu được thực hiện bằng cách sử dụng Dữ liệu cốt lõi, với một số NSFetchedResultsController làm cơ sở cho chế độ xem bảng chi tiết.

Sự cố tôi thấy là nếu bạn chuyển nhanh giữa màn hình chính và màn hình chi tiết nhiều lần trong khi dữ liệu mới đang được truy xuất, phân tích cú pháp và lưu, ứng dụng bị treo hoặc treo hoàn toàn. Dường như có một số loại điều kiện chủng tộc, có thể do dữ liệu Core nhập dữ liệu trong nền trong khi luồng chính đang cố thực hiện tìm nạp, nhưng tôi đang suy đoán. Tôi đã gặp sự cố khi chụp bất kỳ thông tin sự cố có ý nghĩa nào, thường là SIGSEGV sâu trong ngăn xếp Dữ liệu chính.

Bảng dưới đây cho thấy thứ tự thực tế của các sự kiện đó xảy ra khi các chi tiết xem bảng điều khiển được nạp:

 
Main Thread       Background Thread 
viewDidLoad 

            Get JSON data (using AFNetworking) 

Create child NSManagedObjectContext (MOC) 

            Parse JSON data 
            Insert managed objects in child MOC 
            Save child MOC 
            Post import completion notification 

Receive import completion notification 
Save parent MOC 
Perform fetch and reload table view 

            Delete old managed objects in child MOC 
            Save child MOC 
            Post deletion completion notification 

Receive deletion completion notification 
Save parent MOC 

Khi khối hoàn AFNetworking được kích hoạt khi dữ liệu JSON đã đến, một lồng nhau NSManagedObjectContext được tạo ra và chuyển đến đối tượng "nhà nhập khẩu" phân tích dữ liệu JSON và lưu các đối tượng vào kho dữ liệu lõi. Các nhà nhập khẩu thực hiện bằng cách sử dụng phương pháp performBlock mới được giới thiệu trong iOS 5:

NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
    [child setParentContext:self.managedObjectContext];   
    [child performBlock:^{ 
     // Create importer instance, passing it the child MOC... 
    }]; 

Đối tượng nhập khẩu quan sát MOC riêng của nó NSManagedObjectContextDidSaveNotification và sau đó đăng tải thông báo riêng của mình mà được quan sát bởi bộ điều khiển chi tiết bảng điểm. Khi thông báo này được đăng, bộ điều khiển xem bảng thực hiện lưu trên MOC (mẹ) của chính nó.

Tôi sử dụng cùng một mẫu cơ bản với đối tượng "deleter" để xóa dữ liệu cũ sau khi dữ liệu mới cho ngày đã được nhập. Điều này xảy ra không đồng bộ sau khi dữ liệu mới được tìm nạp bởi bộ điều khiển kết quả được tìm nạp và khung nhìn chi tiết của bảng đã được tải lại.

Một điều tôi không làm là quan sát bất kỳ thông báo hợp nhất nào hoặc khóa bất kỳ ngữ cảnh đối tượng được quản lý hoặc điều phối viên lưu trữ liên tục nào. Đây có phải là điều tôi nên làm không? Tôi là một chút không chắc chắn làm thế nào để kiến ​​trúc sư này tất cả một cách chính xác như vậy sẽ đánh giá cao bất kỳ lời khuyên.

Trả lời

2

Chỉ cần một ý tưởng kiến ​​trúc:

Với mẫu refresh dữ liệu đã nêu của bạn (một lần một ngày, chu kỳ đầy đủ của dữ liệu bị xóa và gia tăng), tôi sẽ thực sự có động lực để tạo ra một cửa hàng khăng khăng mới mỗi ngày (tức là đặt theo tên ngày lịch), và sau đó trong thông báo hoàn thành, có thiết lập xem bảng thiết lập một fetchedresultscontroller mới được liên kết với cửa hàng mới (và có thể là một MOC mới), và làm mới bằng cách sử dụng đó. Sau đó, ứng dụng có thể (ở nơi khác, có lẽ cũng được kích hoạt bởi thông báo đó) hoàn toàn phá hủy kho dữ liệu "cũ". Kỹ thuật này tách riêng quá trình xử lý cập nhật từ kho dữ liệu mà ứng dụng hiện đang sử dụng và "chuyển đổi" sang dữ liệu mới có thể được xem xét nhiều hơn nguyên tử, vì thay đổi xảy ra chỉ đơn giản là bắt đầu trỏ đến dữ liệu mới thay vì hy vọng bạn không bắt được cửa hàng ở trạng thái không nhất quán trong khi dữ liệu mới đang được viết (nhưng chưa hoàn thành).

Rõ ràng tôi đã để lại một số chi tiết, nhưng tôi có xu hướng nghĩ rằng nhiều dữ liệu bị thay đổi trong khi đang sử dụng nên được cấu trúc lại để giảm khả năng xảy ra sự cố.

hạnh phúc để thảo luận thêm ...

+0

Tôi thích ý tưởng này, tôi thích nó ** rất nhiều **. Nó cũng có lợi thế là việc xóa tập tin SQLite sẽ nhanh hơn rất nhiều so với việc Core Data xóa các đối tượng được quản lý trong đồ thị đối tượng, mặc dù dĩ nhiên hiệu năng xóa không quan trọng vì một cửa hàng liên tục khác sẽ vẫn được sử dụng. Tôi sẽ đưa ra cách tiếp cận này vào cuối tuần này. –

3

Pre-iOS 5, chúng tôi đã thường có hai NSManagedObjectContexts: một cho các chủ đề chính, một cho một sợi nền. Chủ đề nền có thể tải hoặc xóa dữ liệu và sau đó lưu. Kết quả NSManagedObjectContextDidSaveNotification sau đó được chuyển (khi bạn đang thực hiện) đến luồng chính. Chúng tôi gọi là mergeChangesFromManagedObjectContextDidSaveNotification: để đưa những người vào bối cảnh chủ đề chính. Điều này đã làm việc tốt cho chúng tôi.

Một khía cạnh quan trọng của việc này là các save: trên thread nền khối cho đến sau khi kết thúc mergeChangesFromManagedObjectContextDidSaveNotification: chạy trên các chủ đề chính (vì chúng ta gọi là mergeChanges ... từ người nghe đến thông báo đó). Điều này đảm bảo rằng bối cảnh đối tượng quản lý chủ đề chính thấy những thay đổi đó. Tôi không biết nếu bạn cần để làm điều này nếu bạn có mối quan hệ cha-con, nhưng bạn đã làm trong mô hình cũ để tránh các loại rắc rối khác nhau.

Tôi không chắc lợi ích của việc có mối quan hệ cha-con giữa hai ngữ cảnh là gì. Có vẻ như từ mô tả của bạn rằng lần lưu cuối cùng vào đĩa xảy ra trên chuỗi chính, điều này có thể không lý tưởng vì lý do hiệu suất. (Đặc biệt nếu bạn có thể xóa một lượng lớn dữ liệu, chi phí chính để xóa trong ứng dụng của chúng tôi luôn xảy ra trong lần lưu cuối cùng vào đĩa.)

Bạn đang chạy mã nào khi bộ điều khiển xuất hiện/biến mất có thể gây ra sự cố dữ liệu lõi? Các loại dấu vết ngăn xếp nào mà bạn thấy sự cố xảy ra?

+0

Cảm ơn. Tôi thực sự không sử dụng 'mergeChangesFromManagedObjectContextDidSaveNotification:' vì trong sử dụng bình thường dữ liệu mới hiển thị hoàn toàn tốt mà không có nó. Tuy nhiên, tôi đã sử dụng nó trong một phiên bản trước và có cùng sự cố khi chuyển đổi nhanh giữa hai màn hình. –

2

Vấn đề chính tôi đã có với đa luồng dữ liệu cốt lõi là vô tình truy cập vào một đối tượng quản lý trong một sợi/xếp hàng khác so với cái nó đã được tạo ra trong.

Tôi đã tìm thấy một công cụ gỡ lỗi tốt là thêm NSAsserts để kiểm tra xem các đối tượng được quản lý được tạo trong ngữ cảnh đối tượng được quản lý chính của bạn chỉ được sử dụng ở đó và các đối tượng được tạo trong ngữ cảnh nền không được sử dụng trong ngữ cảnh chính.

này sẽ liên quan đến subclassing NSManagedObjectContext và NSManagedObject:

  • Thêm một Ivar đến lớp con MOC và gán cho nó hàng đợi nó được tạo ra về.
  • Phân lớp MO của bạn nên kiểm tra hàng đợi hiện tại giống như thuộc tính hàng đợi của MOC.

Nó chỉ là một vài dòng mã, nhưng về lâu dài có thể ngăn bạn mắc lỗi khó theo dõi ngược lại.

+0

Cảm ơn, nhưng tôi không chắc chắn làm thế nào để có được hàng đợi của MOC vì nó được khởi tạo bằng cách sử dụng 'initWithConcurrencyType: NSPrivateQueueConcurrencyType' có nghĩa là nó tạo ra hàng đợi công văn riêng của nó. –

+0

Làm thế nào về dispatch_get_current_queue bên trong performBlock của bạn: –

+0

Hàng đợi đó là riêng tư; cố gắng truy cập trực tiếp vào nó là thứ mà người Apple gọi là một ý tưởng tồi. –

2

NSFetchedResultsController đã được chứng minh là có một chút nhạy cảm với việc xóa lớn nên đó là nơi tôi sẽ bắt đầu đào trước.

Câu hỏi ban đầu của tôi là, cách tìm nạp lại và tải lại tableview liên quan đến bắt đầu thao tác xóa. Có khả năng là khối xóa sẽ lưu MOC con trong khi NSFetchedResultsController vẫn đang tìm nạp hay không?

Có thể khi bạn chuyển từ chế độ xem chi tiết sang chế độ chính và sau đó quay lại chế độ xem chi tiết, sẽ có nhiều tác vụ nền đồng thời chạy?Hay bạn đang truy xuất tất cả dữ liệu từ dịch vụ web cùng một lúc không chỉ có liên quan đến một hàng cụ thể?

Một thay thế để làm điều này mạnh mẽ hơn là sử dụng một mô hình tương tự như những gì UIManagedDocument sử dụng:

Thay vì sử dụng một MOC mẹ như chính loại Chủ đề đồng thời, UIManagedDocument thực sự tạo ra các MOC chính như hàng đợi tin và làm cho MOC con có sẵn cho bạn sử dụng trên chuỗi chính. Lợi ích ở đây là tất cả các I/O được thực hiện ở chế độ nền và lưu vào MOC mẹ không can thiệp vào MOC của trẻ cho đến khi MOC trẻ rõ ràng được biết về chúng. Đó là bởi vì lưu cam kết thay đổi từ con sang cha mẹ và không phải là cách khác xung quanh.

Vì vậy, nếu bạn đã xóa trên hàng đợi gốc là riêng tư, điều đó sẽ không kết thúc ở phạm vi NSFetchedResultsController. Và vì đó là dữ liệu cũ, đó thực sự là cách ưa thích.

Một thay thế tôi đưa ra là sử dụng ba tình huống:

chính MOC (NSPrivateQueueConcurrencyType)

  • Chịu trách nhiệm cửa hàng liên tục và xóa các dữ liệu cũ.

Child MOC Một (NSMainQueueConcurrencyType)

  • Chịu trách nhiệm bất cứ điều gì UI liên quan và NSFetchedResultsController

Child MOC B (NSPrivateQueueConcurrencyType, con của con MOC A)

  • Chịu trách nhiệm cho việc chèn dữ liệu mới và cam kết nó lên Child MOC Khi hoàn tất.
Các vấn đề liên quan