2017-05-06 1 views
1

, 그것은 배경 멀티 스레드 문제를 처리하기 위해Swift 3에서 코어 데이터 동시성이 멀티 스레딩과 어떻게 작동합니까? 내 프로그램에서

DispatchQueue.global(qos: .background) 

self.concurrentQueue.sync(flags: .barrier) 

을 모두 사용합니다. 나는 또한 그런

문제가 발생 디버깅 할 -com.apple.CoreData.ConcurrencyDebug 1 수 있도록

lazy var context: NSManagedObjectContext = { 
    return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.newBackgroundContext() 
}() 

:

이 3 그래서 나는 childContext을 얻을 수있는 최신 방법을 사용하여 신속입니다

1을 참조하면, 거기이다 API 호출 및 콜백 블록 (백그라운드 스레드)에서 핵심 데이터를 가져 와서 편집 한 다음 저장해야합니다. 위의 코드에서 self.context를 호출하여 performBlockAndWait을 호출하고이 블록 내부에 save을 넣으려고했습니다. 전체 프로세스가 잘 진행되지만이 블록 외부에서 결과를 액세스하려고 시도하지만 콜백 블록 내부에서 오류가 발생합니다. 나는 또한 objectId와 getObjectById을 self.context와 self.context.parent로 가져 오려고 시도했으며이 줄에서 오류가 발생합니다. 내가 뭘 잘못했는데 어떻게해야합니까? 왜냐하면 나는 많은 다른 스레드 (컨텍스트가 아님)에서 결과를 사방에 사용해야하기 때문입니다.

2, 게시물마다 스레드 당 하나의 컨텍스트가 필요하다고 말하면서, 내 경우에는 API 호출로부터의 콜백이라면 어떤 정확한 스레드인지를 어떻게 결정할 수 있습니까?

3 내 프로그램이 백그라운드 스레드에서 실행되어야하므로 (다른 게시물에서 읽음)이 방법으로 수행해야하므로 privateConcurrentType이 필요한 이유는 무엇입니까?

4, 내 질문에 1, 내 상황에서 여전히 작동하지 않는 다른 컨텍스트로 objectId 전달하여 개체를 가져옵니다. 이것이 적절한 방법이라고 가정합시다. 슈퍼 유저가 아닌 다른 스레드에서 내 전체 프로그램 전체에 걸쳐 너무 많은 objectID를 전달하는 것을 어떻게 처리할까요? 나에게 이것은 미친 소리지만,이 문제를 다루는 훨씬 더 깔끔하고 쉬운 방법이 있다고 생각한다.

5, 일부 게시물은 꽤 오래되었습니다 (스위프트 3 이전). childContext.save (parentContext.save)이 필요하지만 위의 코드 (스위프트 3에만 해당)를 사용하고 있습니다. 그것은 작동하도록 childContext.save 할 수있는 것? 내가 맞습니까?

+0

백그라운드 스레드의 컨텍스트에 액세스하려고하면 "오류"가 정확히 "발생"합니까? –

답변

3

코어 데이터는 일반적으로 멀티 스레딩이 가능하지 않습니다. 동시 스레드에서 사용하려면 나쁜 일만 발생한다고 생각할 수 있습니다. 컨텍스트가있는 스레드 외부의 관리 대상 객체를 조작하는 것만으로는 안됩니다.

앞서 언급했듯이 대부분의 경우 작동 할 스레드마다 별도의 컨텍스트가 필요하지만, 필자 경험상 읽기 쓰기가 가능한 배경 컨텍스트 하나와 가져 오기에 사용되는 단일 주 스레드 읽기 전용 컨텍스트 만 있으면됩니다 결과 컨트롤러 또는 다른 인스턴트 페치.

컨텍스트는 데이터베이스 (파일)와 통신하는 일부 메모리 모듈로 생각하십시오. 가져온 엔티티는 컨텍스트 내에서 공유되지만 컨텍스트 간에는 공유되지 않습니다. 따라서 컨텍스트 내에서 내용을 수정할 수는 있지만 컨텍스트를 데이터베이스에 저장할 때까지는 데이터베이스 나 다른 컨텍스트에 표시되지 않습니다. 그리고 2 개의 컨텍스트에서 동일한 엔티티를 수정 한 다음 저장하면 충돌이 발생하여 해결할 수 있습니다.

이 모든 것들은 코드 논리에서 상당히 혼란스럽고 다중 문맥은 피할 수있는 것처럼 보입니다. 내가하는 일은 배경 컨텍스트를 만든 다음 해당 컨텍스트에서 모든 작업을 수행하는 것입니다. 문맥은 (배경 문맥을 위해) 메인이 아니고이 스레드가 직렬 인 자체 스레드에서 코드를 실행할 방법 perform을 가지고 있습니다.

예를 들어 스마트 클라이언트를 할 때 서버에서 새 항목으로 응답을받습니다. 이것들은 즉석에서 구문 분석되며 문맥에서 블록을 수행하여 데이터베이스에있는 모든 해당 객체를 가져 와서 존재하지 않는 객체를 만듭니다. 그런 다음 데이터를 복사하고 컨텍스트를 데이터베이스에 저장하십시오.

UI 부분은 비슷합니다. 엔트리가 저장되면 백그라운드 컨텍스트 스레드에서 엔티티를 생성하거나 업데이트합니다.

public func performBlockOnBackgroundContextAndReturnOnMain(block: @escaping (() -> Void), main: @escaping (() -> Void)) { 
     if let context = context { 
      context.perform { 
       block() 
       DispatchQueue.main.async(execute: {() -> Void in 
        main() 
       }) 
      } 
     } 
    } 

그래서 거의 핵심 데이터 로직의 모든 배경에 단일 스레드에서 발생 : 나는 방법을 그래서 그런 보통 완료에 대한 몇 가지 UI 물건을한다. 어떤 경우에는 주 콘텍스트를 사용하여 예를 들어 결과 컨트롤러를 가져올 수 있습니다. 개체 목록을 표시하고 사용자가 항목 중 하나를 선택하면 해당 항목을 백그라운드 컨텍스트에서 다시 가져와 사용자 인터페이스에서 사용하고 수정합니다.

그러나 일부 속성이 데이터베이스에서 느리게로드 될 수 있으므로 문제가 발생할 수 있으므로 필요한 모든 데이터가 컨텍스트에로드되고 주 스레드에서 액세스 할 수 있도록해야합니다. 그 방법이 있지만 래퍼를 사용합니다.

id만을 포함하는 데이터베이스 모델의 모든 엔티티에 대해 하나의 수퍼 클래스가 있습니다. 그래서 나는 나머지 래퍼와 함께 작동하는 모든 로직을 가진 수퍼 클래스 래퍼를 가지고있다. 마지막으로 남은 것은 각 하위 클래스에 대해 관리 대상으로부터 시작하여 2 개의 매핑 방법을 재정의해야한다는 것입니다.

추가 래퍼를 만들고 관리되는 개체에서 메모리로 데이터를 복사하는 것은 어리석은 것처럼 보일 수 있지만 어쨌든 대부분의 관리되는 개체에 대해서는이를 수행해야합니다. NSDataUIImage/NSDate/Date으로 변환하거나, 정수 또는 문자열과의 열거 ... 결국에는 1 : 1로 복사 된 문자열로 끝나게됩니다. 또한이 클래스에서 서버의 응답을 매핑하는 코드 나 관리 대상 객체와의 이름 충돌이없는 추가 로직을 쉽게 만들 수 있습니다.

관련 문제