일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- Swift LinkedList
- Swift 고차함수
- 일급 객체
- Persistent store Coordinator
- iOS Static Library
- Java
- Swift
- 스위프트 클로저
- Associated Value
- 트레일링 클로저
- dateFormatter
- LightWeight Migration
- CoreData Concurrency
- CoreData Filter
- codability
- 2022 부스트캠프
- Raw value and Associated value
- persistentStoreCoordinator
- NSManagedObject SubClass
- NSPredicates
- iOS Static Library 사용하는방법
- CoreData Stack
- leetcode #01
- CoreData
- Clean swift
- expensive operation
- Swift closure
- NSSortDescriptor
- 다익스트라 이해
- 1009번
- Today
- Total
하루를살자
[iOS] CoreData 를 배워보자 - 5 [CoreData Concurrency] 본문
목차
1.0 Motivation: persistent container 의 viewContext 는 어떤 Thread 에서 동작하나?
1.1 Managed Object Context Concurrency Types
2.0 Heavy Duty Task Strategy
2.1 performBackgorundTask(_:) via Persistent Container
2.2 newbackgroundContext()
2.3 Child context
3.0 Performance Test
1.0 Motivation: persistent container 의 viewContext 는 어떤 Thread 에서 동작하나?
- 기본적으로 생성된 Persistent container 의 viewContext(NSObjectManagedContext) 는 mainQueueConcurrencyType(main queue) 에서 동작합니다.
- 따라서 만약 어떠한 Task 에 관련된 데이터가 많거나 클경우 UI 에 직접적인 영향을 주어 사용자가 앱을 사용하는데 불편함을 겪을수 있습니다.
- 보통 이런문제는 GCD 를 이용하여 backgroundQueue 에 오래걸리는 작업을 수행하는데, CoreData 는 Thread-Safe 하지 않기 때문에 그냥 backgroundQueue 에 Task 를 수행시킬수 없습니다.
1.1 Managed Object Context Concurrency Types
💭 그럼 어떻게 background task 를 수행시킵니까?
👉 CoreData 는 NSManagedObjectContext 를 생성할때 어떤 Queue 에서 동작할건지 미리 정해줄수 있도록 생성자에 명시할수 있습니다.
- 크게 2가지로 분류되는데, 앞서 설명드린 mainQueueConcurrencyType 과 privateQueueConcurrencyType 가 있습니다.
- mainQueue = main queue 에서 작업을 수행.
- privateQueue = background queue 에서 작업을 수행
- 이렇게 생성된 Context 들은 2가지 다른 메소드(perform(_:), performAndWait(_:)) 를 통해 원하는 작업을 context 에 해당하는 Thread 에서 진행 시킬수 있습니다.
- 이 둘 메소드의 차이점은 클로저에 선언된 명령어를 어떤식으로 queue 가 처리하는지에 있습니다.
- perform(_:) = 메소드 호출이후 바로 다음 흐름으로 넘어갑니다. Thread 가 호출이 끝날때까지 기다리지 않아도 됩니다. (비동기)
- performAndWait(_:) = 메소드 호출 이후 끝날때까지 기다립니다. (동기)
2.0 Heavy Duty Task Strategy
- mainQueueConcurrencyType 을 사용하는 대표적인 예는 디폴트로 생성되는 Persistent container 의 viewContext 가 있습니다.
- 앞서 설명했듯이 많은양의 데이터를 다루는 Task 를 여기서 진행하게된다면 UI 를 Block 하기 때문에 concurrent queue 에서 이러한 작업을 진행시켜주어야합니다.
- 이포스트에선 아래 3가지 방법으로 Heavy lifting 작업을 수행하는 법을 알아보겠습니다.
2.1 performBackgorundTask(_:) via Persistent Container
- 만약 현재 persistent container 가 존재한다면, 간단히 performBackgroundTask 를 사용해서 background queue 을 통해 작업을 처리할수 있습니다. 해당 메소드는 클로저를 통해서 context 를 넘겨주는데 이는 현재 persistent store 에 연결되어있는 private queueConcurrency Type 입니다.
- 해당 context 를 이용해서 변경된 내용을 저장하면 mainContext 에 저장된 내용이 업데이트 되어집니다.
- 변경사항이 mainContext 에 적용된이후, disk 에 저장하고 싶다면 따로 viewContext.save() 를 호출하여 저장해야합니다.
func saveContext() {
persistentContainer.performBackgroundTask { context in
do{
try context.save()
//만약 변경된 내용을 disk 에 저장하고 싶다면
try persistentContainer.performAndwait {
try persistentContainer.viewContext.save()
}
}catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
2.2 newbackgroundContext()
- 이 메소드 는 존재하는 persistent container 를 사용하여 같은 persistentStoreCoordinator 를 가르키는 새로운 context 를 생성 시킵니다.
- 이때 생성되는 context 를 backgroundManagedObjectContext 라고 불른다고 치고, parent context 를 살펴보면 nil 이 찍히는것을 알수 있습니다. (viewContext 의 parent 또한 nil)


- 아래와 같이 perform 메소드를 사용해서 비동기적으로 데이터를 private queue 에서 처리할수 있습니다.
//create new background context
var backgroundContext = persistentContainer.newbackgroundcontext()
backgroundContext.perform {
do {
try backgroundManagedObejctContext.save()
//변경된 사항을 disk 에 저장하고 싶다면
try persistentContainer.viewContext.performAndWait {
try persistentContainer.viewContext.save()
}
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
2.3 Child context
- 또하나의 방법은 하나의 context 에 private queue 로되어 있는 parent 혹은 child 를 만들어서 작업을 대신 처리하게 하는 것 입니다.
- 이방법은 persistentContainer 를 사용하는지, 아니면 커스텀한 coredata Stack 을 사용하느냐에 따라서 parent 가 heavy lifting 작업을 수행할수도, child context 가 작업을 처리할수도 있게 설계할수 있습니다. (각각의 상황에 맞추어서 설계하는게 중요!)
- 예제는 persistentContainer 를 사용해서 childContext 를 priavte queue type 으로 생성해 주겠습니다.

- 새로운 context 를 viewcontext 의 parent 가 아닌 childContext 로 설정한이유는 child context 는 parent context 의 persistentStore Coordinator 를 사용하게 되는데, persistentContainer 의 viewContext 는 readOnly 이기 때문에 새로운 persistentStore Coordinator 를 지정할수없게 됩니다. (만약 강제로 하게 된다면 아래와 같은 에러를 받게됩니다)
let saveManagedObejctContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
persistentContainer.viewContext.parent = saveManagedObejctContext
//Error!
//*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
//reason: 'Context already has a coordinator; cannot replace.'
- 아래 코드는 새로운 context 를 private Queue 타입으로 만들고, persistentContainer 를 parent 로 지정하여 데이터를 저장하는 로직입니다.
lazy var backgroundManagedObejctContext: NSManagedObjectContext = {
let saveManagedObejctContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
saveManagedObejctContext.parent = persistentContainer.viewContext
saveManagedObejctContext.automaticallyMergesChangesFromParent = true //Parent context 에서 바뀐 상태를 자동으로 머지함.
return saveManagedObejctContext
}()
backgroundManagedObejctContext.perform {
do {
try backgroundManagedObejctContext.save()
try persistentContainer.viewContext.performAndWait {
try persistentContainer.viewContext.save()
}
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
3.0 Performance Test
- 아래 예제는 이미지를 선택하고 뒤로 가기를 누를때 CoreData 의 Save() 를 호출합니다. 이때 Save() 함수가 걸리는 시간을 main queue 를 사용했을때와 private queue 를 사용했을때의 차이를 측정해 보겠습니다.

- MainQueue

- PrivateQueue

- private queue 를 사용해서 저장하는데 거의 100배 빠른 응답을 주었습니다. 아무리 ms 가 걸린다고 해도 데이터 양이 방대해지면 private queue 와 main queue 의 사용 용도를 분명히 나누어서 프로젝트를 진행해야겠습니다.
참조
https://www.kodeco.com/7586-multiple-managed-object-contexts-with-core-data-tutorial
'iOS' 카테고리의 다른 글
[iOS] Static Library 사용하는방법 (0) | 2022.12.23 |
---|---|
[iOS] Static Library 만들기 (using Objective-C) (1) | 2022.12.22 |
[iOS] CoreData 를 배워보자 - 4 [LightWeight Migration] (0) | 2022.10.18 |
[iOS] CoreData 를 배워보자 - 3 [NSFetchRequest, Relationship] (0) | 2022.10.18 |
[iOS] CoreData 를 배워보자 - 2 (0) | 2022.10.14 |