2012-11-02 2 views
9

우리는 코어 데이터를 사용하여 수만 개의 객체를 저장하는 엔터프라이즈 수준의 응용 프로그램을 개발하고 있으며 여러 가지 문제가 있습니다.코어 데이터 관리 객체 컨텍스트 디자인 권장 사항


우리의 응용 프로그램에는 필요할 때 데이터를 조작하는 몇 가지 독립적 인 시스템이 있습니다. 이러한 시스템에는 항목 검색, 항목로드, 동기화 및 UI 표시가 포함됩니다. 우리가 소프트웨어를 올바르게 설계한다면, 같은 시스템을 수정하는 다른 시스템으로 인해 아무도 충돌을 병합하지 않아야합니다. 각 시스템에는 고유의 작업 대기열이 있으며 모두 백그라운드에서 수행됩니다. 특히 초기 램프 업 (initial ramp up) 중에 UI 성능 문제를 최소화하기 위해 백그라운드에서 모든 객체 생성 및 수정을 유지하고자합니다. 수천 개의 객체가 서버의 데이터에서 생성 될 수 있습니다. 여기 우리는 다양한 디자인 시도에 몇 가지 문제점을 안고 있습니다. 이러한 ramp가 진행되는 동안 거대한 메모리 소비와 모든 컨텍스트와 자식 컨텍스트의 잘못된 조정으로 인해 교착 상태와 충돌이 발생합니다. 우리는 다음과 같은 디자인을 시도했다 :

  • 하나의 루트를 한 아이 NSMainQueueConcurrencyType 컨텍스트를 가지고 NSPrivateQueueConcurrencyType 관리되는 개체 컨텍스트를. UI 가져온 결과 컨트롤러는이 하위 컨텍스트를 사용하여 결과를 가져옵니다. NSMainQueueConcurrencyType 자식 컨텍스트에서 우리는 "savingContext"라고하는 하나의 NSPrivateQueueConcurrencyType 자식 컨텍스트를 만들고 각각의 백그라운드 작업은 해당 "savingContext"의 자식 컨텍스트를 만들고 변경 한 다음 마지막으로 "깊은 저장"이라고하는 것을 반복적으로 수행했습니다 상단에 저장합니다. 처음에는 많은 다른 하위 컨텍스트의 알림 NSManagedObjectContextDidSaveNotification을 처리 할 필요가 없도록이 디자인을 선택했습니다. 우리는 NSPrivateQueueConcurrencyType 컨텍스트에 대한 모든 호출을 포장하고 performBlockAndWait:으로 개체에 액세스했습니다. 기능적으로이 디자인이 수행되었습니다. 모든 변경 사항 및 삽입이 영구 저장소에 저장되었으며 UI가 변경 사항으로 업데이트되었습니다. 이것은 두 가지 이슈를 소개했습니다. 하나는 NSMainQueueConcurrencyType 자식 컨텍스트를 통해 병합 된 변경으로 인해 램프 업 중 느린 UI 였고 더 중요한 것은 램프 업 중 매우 높은 메모리 사용량 이었기 때문입니다. 우리는 reset을 컨텍스트 (메인 UI 하위 컨텍스트가 너무 있음)로 호출 할 수 없거나 refreshObject:mergeChanges:을 호출 할 때 지식이 부족하기 때문에 금지 된 RAM 사용법을 쳤습니다. 그래서 우리는 다른 길을갔습니다.
  • 영구 저장소 조정자와 연결된 두 개의 최상위 컨텍스트, 하위 컨텍스트 저장을위한 NSPrivateQueueConcurrencyType 및 UI 표시를위한 NSMainQueueConcurrencyType이 있습니다. NSMainQueueConcurrencyType은 기본 NSPrivateQueueConcurrencyType 컨텍스트에서 NSManagedObjectContextDidSaveNotification 개의 알림을 수신하고 주 스레드에서 병합합니다. 각 백그라운드 작업은 기본 NSPrivateQueueConcurrencyType 컨텍스트의 하위 컨텍스트를 만듭니다. 또한 개인 큐 동시성 유형을 사용하여 수행하고, "깊은 저장"을 반복적으로 수행하여 현재 컨텍스트에서 저장을 수행하고, 해당 컨텍스트로 깊은 저장을 재귀 호출합니다. 부모는 현재 컨텍스트에서 다시 설정을 호출하고 다시 저장합니다. 이렇게하면 생성 된 객체가 저장 후에 빠르게 해제되므로 메모리 문제를 피할 수 있습니다. 그러나이 디자인에서는 NSMainQueueConcurrencyType 컨텍스트에 대한 알림을 저장하고 있음에도 불구하고 데드 록, NSInternalInconsistencyException 예외 및 가져온 결과 컨트롤러와 같은 많은 문제가 발생했습니다. 또한 UI의 초기로드 시간이 많이 느려집니다. 이전 디자인에서 가져온 결과 컨트롤러는 결과를 매우 빠르게 반환했지만보기가로드 될 때까지 UI가 몇 초 동안 차단되었습니다 (가져온 결과 컨트롤러를 viewDidLoad으로 초기화).

가져온 결과 컨트롤러가 UI 또는 교착 상태와 NSInternalInconsistencyException 예외를 업데이트하지, 중 매우 높은 메모리 사용, 우리는 많은 중간 디자인을 시도했지만 모두 같은 문제를 중심으로 돌고.


나는 정말로 좌절하고 있습니다. 나는 우리의 디자인이 다소 단순해야만하는 것에 대해 명백하게 복잡 해지는 것처럼 느낄 수는 없으며, 우리를 죽이는 기본 사항을 이해하지 못하는 것입니다.


그래서 너희들은 무엇을 제안? 우리의 맥락에서 어떤 배열을 권하고 싶습니까? 다른 스레드에서 서로 다른 컨텍스트를 어떻게 관리해야합니까? 삽입 된 객체를 해제하고 컨텍스트를 재설정하는 모범 사례? 죽은 자물쇠를 피하는가? 이 시점에서 모든 도움을 주시면 감사하겠습니다.


또한 MagicalRecords 카테고리에 대한 권장 사항을 보았습니다. 그것은 권위가 있습니까? 우리는 이미 핵심 데이터 유형을 사용하는데 투자를하고 있으며, MR을 사용하여 마이그레이션하는 것이 얼마나 어려울까요?

+0

비슷한 문제 (아키텍처 하나)가 있습니다. http://stackoverflow.com/questions/15999932/core-data-break-retain-cycle-of-the-parent-context/16008470 - 아키텍처 종류 결국에 갔니? 어떤 팁? – Zyphrax

+0

주 컨텍스트와 상위 루트 컨텍스트가 있습니다. 하지만 루트 컨텍스트는 저장에만 사용되므로 모든 저장시에 다시 설정됩니다. 우리에게는 무수한 이슈가 있습니다. 아이와 부모 컨텍스트의 이러한 새로운 기능은 잘 알려지지 않았으며 많은 버그가 여전히 Apple에 의해 무인으로 남아 있습니다. –

+0

자식 MOC (MainQueue)를 사용하여 루트 MOC (PrivateQueue)로 아키텍처를 배우고'''[self.managedObjectContext refreshObject : self mergeChanges : NO];'''를 호출하여 메모리를 줄입니다. 내 ManagedObjects에서'-didSave'''를 사용합니다. 이렇게하면 관계 사이의 유지주기가 분리되고 모든 MOC가 오브젝트 할당을 해제 할 수 있습니다. NSPrivateQueueConcurrencyType을 가진 MOC 만 개체를 ​​즉시 할당 해제하지 않지만 다음 저장/롤백시 이상한 문제가있는 것 같습니다. – Zyphrax

답변

6

먼저, 메모리를 관리하기 위해 두 번째 아키텍처는 유연성을 제공합니다.

두 번째로 관리 할 메모리는 malloc-ed 메모리와 resident VM 메모리입니다. malloc 화 된 메모리 공간이 적지 만 여전히 큰 VM 상주 영역을 가질 수 있습니다. 내 경험에 따르면 새로 삽입 된 항목을 적극적으로 보유하고있는 Core Data 때문입니다. 포스트 삭제 트리밍 알림으로이 문제를 해결합니다.

셋째, MOC는 저렴합니다. 꺼내어 쓰다. 즉, 일찍 그리고 자주 메모리를 해제하십시오.

넷째, 주 MOC에서 거의 아무런 데이터베이스도 시도하지 마십시오. 예, 이것은 비생산적인 것으로 들립니다. 제 말은 모든 복잡한 쿼리가 백그라운드 스레드에서 실제로 수행되어야하고 결과가 메인 스레드로 전달되거나 현재 채워진 행 캐시를 이용하는 동안 쿼리를 메인 스레드에서 다시 실행해야한다는 것입니다. 이렇게하면 UI를 라이브로 유지할 수 있습니다.

다섯째, 대기열이 많은 대기열에있는 앱에서 다섯 번째로 백그라운드에서 실제로 내 저장을 모두 시도하려고합니다. 이것은 내 주요 MOC를 빠르게 유지하고 그물에서 들어오는 데이터와 일치합니다.

여섯째, NSFetchedResultsController는 매우 유용하지만 특수화 된 컨트롤러입니다. 애플리케이션이 역량 범위를 벗어나면 애플리케이션을 잠그기 시작합니다. 그런 일이 발생하면 직접 -didSave 알림을 수신하여 컨트롤러를 롤백합니다.

+0

답변 해 주셔서 감사합니다. 락업은 어떨까요? 현재 우리 앱은 때때로 우리에게 불분명 한 이유로 잠겨 있습니다. 통상은, 통지 후에'mergeChangesFromContextDidSaveNotification :'를 호출 할 때 락온됩니다. 여러 스레드가 동시에 저장할 수 있습니까? 컨텍스트가 잠겨 있어야합니까? –

+0

레오, UI 변경 사항을 절대로 저장하지 않기 위해 UI가 주 MOC에 갇혀있는 시간을 최소화합니다. 나는. 새로운 데이터가 백그라운드에서 병합됩니다. 그것은 주 MOC를 무료로 유지합니다. 또한 페치 된 결과 컨트롤러를 업데이트 중이거나 MOC를 병합했기 때문에 잠금 장치가 작동하는지 테스트하십시오. FRC 대리자를 설정하지 않아도 테스트 할 수 있습니다. 예. 여러 스레드가 동시에 저장할 수 있습니다. SQLite는 단일 스레드 저장소이기 때문에 PSC를 통해 모든 것이 직렬화됩니다. 결과적으로 내 배경 MOC를 통해 글쓰기/저장을 직렬화합니다. 앤드류 – adonoho

관련 문제