https://onelife2live.tistory.com/35
잘못된 내용이나 추가할 개념이 있다면 댓글로 남겨주시면 감사하겠습니다🙏🏻
Core Data를 알고싶어서 최근 WWDC를 참고했습니다.
개념도 쉽게 받아들이기 어렵기도하고,
정말 정말 다양한 기능들이 있더라구요.
물론 아직 알아보지못한 기능도 많습니다. 앞으로 차근차근 배워나가야죠.
- ( 확실히 아는만큼 보인다가 맞는것같아요. 모를때는 우물안 개구리처럼 사용했었거든요 ㅠ )
그래서 좀 더 간결하게, 필요한내용들만 정리하고자 작성합니다.
밑줄친부분들은 링크를 걸어두었습니다.
Core Data 가 무엇인지, 느낌을 알고싶다면, 각 세션들을 참고해보면 좋을것같아요.
제가 작성한 글을 보셔도 괜찮지만, 아무래도 직접 보는게 더 와닿으니까요.
그리고 해당 세션들을 본다고해서 바로 Core Data를 적용할수있는건 아니였습니다..크흡.. 따로 공식문서들을 참고해야해요.
WWDC 17 - What's New in Core Data
WWDC 18 - Core Data Best Practices
WWDC 19 - Making Apps with Core Data
WWDC 20 - Core Data: Sundries and maxims
간단한 기능들을 연습하기 위해 따로 깃헙에 올려놨습니다. 참고용으로만 봐주시면 좋을것같습니다.
https://github.com/gustn3965/PracticeCoreData
CoreData
우선 Core Data는 디스크에 저장하기위한 데이터베이스라고 생각하면된다. 즉 디바이스에 영구적으로 저장하기위한 목적이다.
크게 Persistent store , managed obejct Context, Manged object Model로 구성되어있다.
Core Data를 이용하기위해서는 위의 개념들을 세팅하는 작업이 필요한데, 코드도 길고, 예외처리도 많고 불편하기때문에
손쉽게 추상화한 Persistent Container를 이용하여 세팅할수있다.
Persistent Container
- 손쉽게 core data 스택을 세팅해준다.
- persistent store들을 load한다.
- model만따로 load할수도있다
- 서브클래싱이 가능하다. ( 주로 서브클래스하여 사용한다. )
구성요소
- Persistent Store Coordinator
- manged object model, persistent store를 관리하고, request를 수행하는 역할을 한다.
- Manged object Context
- 어떤 작업을 하든간에 context를통해서 이루어져야한다.
- viewContext는 main queue context이다.
- 백그라운드에서도 가능한 context가 있다.
- 데이터처리가많은 경우 ( JSON데이터로부터 Core Data에 넣는경우 등 ) background context를 이용하자.
- 주로 perform or performAndWait 클로저 블록으로 작업들을 작성한다.
- 이 안에서 작성한 작업들은 해당 context의 쓰레드에서 실행되도록 보장한다.
- 그러므로, 데이터를 가져오는 메서드가 viewContext로 매번 save한다고 가정하자. viewContext가 메인쓰레드여서 perform블록을 사용하지 않을 수 있는데, 이런 경우 이 메서드를 사용하는 곳이 항상 메인쓰레드일 거라는 보장은 없다. ( 예를 들어 웹 네트워킹을 한다면, 백그라운드 쓰레드를 사용하게 되고, 이때 해당 메서드를 호출한다면 안전하지 않다. 그러므로 무조건 확실하다는 경우외에, 그래도 항상 안전하게 특정 context에서 perform 블록을 사용하도록 하자.
- 어떤 작업을 하든간에 context를통해서 이루어져야한다.
- Manged object Model
- 말그대로 저장할 데이터를 코드로 구체화한 model로써, Xcode 에디터로 추가할수있고, 코드로도 추가할수있다.
- Xcode로 추가하게되면 자동적으로 NSManagedObject를 채택하는 class가 만들어진다. 물론 이를 extension과도 활용할수있다.
- 각 특정값들을 저장하기위한 attribute로 구성되어있다.
- 각 Model들은 releationShip을 설정할수있다.
- 이를통해 1대1, 1대다, 다대1, 다대다로 구성할수있으며, 하나의 model이 삭제된다면 연관된 relationship들도 같이 삭제하거나, 이러한 룰을 적용할수있다. 굉장히 편리한기능! ( 아래와같이 화살표로 이루어져있어요. )
- 말그대로 저장할 데이터를 코드로 구체화한 model로써, Xcode 에디터로 추가할수있고, 코드로도 추가할수있다.
( 1 ) NSPersistentContainer 사용방법
iCloud를 이용한다면 다음과 같이 containter를 세팅할 수 있고,
iCloud를 이용하지 않는다면 NSPersistentContainer를 이용하면 된다.
loadPersistentStore를 호출하면 persistentStore들을 찾아서 로드해주고,
만약 없는 경우에는 새로 만들어준다.
여기에서 container에 특정 persistentStore들을 지정하여 그들만 로드하도록 할 수도 있다.
( 2 ) 개별적으로 세팅하는 방법
간단하게 NSPersistentContainer를 이용하여 CoreData stack을 세팅할 수 있지만,
정말 CoreData로 이것 저것 하다보면은 그 구조와 개별적으로 사용해야하는 경우도 생기므로 알아두면 좋다.
NSManagedObjectModel
- Entity들이 담겨있는 .xcdatamodel을 찾는다.
guard let modelURL = Bundle.main.url(forResource: "DataModel",
withExtension: "momd") else {
fatalError("Failed to find data model")
}
guard let mom = NSManagedObjectModel(contentsOf: modelURL) else {
fatalError("Failed to create model from file: \(modelURL)")
}
NSPersistentCoordiantor
- Coordinator가 사용할 Object Model과 함께 초기화한다.
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
NSPersistentStore
- persistentStore가 있는 경로로 persistentStore를 coordinator에 추가한다.
let dirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
let fileURL = URL(string: "DataModel.sql", relativeTo: dirURL)
do {
try psc.addPersistentStore(ofType: NSSQLiteStoreType,
configurationName: nil,
at: fileURL, options: nil)
} catch {
fatalError("Error configuring persistent store: \(error)")
}
NSManagedContext
- Context의 타입을 지정하여 초기화하고,
- Context가 사용할 Coordinator를 지정한다.
let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
moc.persistentStoreCoordinator = psc
데이터 생성
- 간단하게 Model을 init하여 context.save 해주면된다.
데이터 삭제
- 간단하게 context.delete ( ) 하면된다.
- ( 강제언래핑했지만 실제에서는 지양해야겠죠! )
Fetch Request
항상 데이터를 fetch할때는 Fetch Request를 작성해야한다. 다양한 옵션들 ( fetchLimit, fetchBatchSize, fetchOffset ) 이 있다.
- fetchLimit는 request에 조건에 해당하는 데이터들중에서 fetchLimit만큼만 가져오겠다.
- fetchBatchSize는 CoreData가 한번에 모든 요청데이터를 가져오지않도록 하기위해, 정하는 사이즈다. fetchBatchSize가 2라면 처음에 2개만가져오고, 그이후 2개가져온다, 또 그이후...
- 그러므로 테이블뷰와 같이 데이터를 보여줄경우, 스크롤을하지않는다면 그이상의 데이터는 가져오지않도록 FetchBatchSize를 정한다.
- 참고 - https://stackoverflow.com/questions/52115696/swift-core-data-fetchbatchsize-vs-fetchoffset
- fetchOffset은 조건에 해당하는 데이터들중에서 offset이후부터 가져온다. fetchOffset이 2라면, 앞에 2개는빼놓고 그이후부터 가져온다.
- NSPredicate를 이용하여 쿼리문처럼, 필요한 데이터들만 가져온다.
- NSPredicate의 문법은 민소네님 블로그에 잘 나와있다.
- 또한 여러개의 NSPredicate를 나눠서 작성할수도있다
- NSSortDescriptor를 이용하여 데이터들을 sort할수있다.
- 또한 NSExpression를 통해서, 추가적인 합계연산( count, sum, average ) 들을 이용하여 새로운 attribute를 만들수있다 할수있다 ( count, sum ... )
- 또한, Combine과 결합되어, publisher를 얻을수있고, 즉각 변경사항들을 반영할수있다.
- 즉 tag의 name이 바뀌면 즉각적으로 tagLabel에도 반영이된다!! ( 실제로 해보니까, 함수로 호출하면 tag는 바로 메모리에서 사라지니까 text에 반영이되자마자 바로 사라졌는데, 따로 프로퍼티에 저장하도록 하니 가능했다!! )
- 결과들을 다양한 resultType으로 나타낼수있다.
- 주의할점은 기본적으로 managedObjectResultType이라는점과, 그외를 사용하고자한다면, fetchRequest를 만들어낼때, 타입을 미리 명시하거나, NSFetchRequestResult로 명시해야한다.
- countResultType을 이용하면, 손쉽게 찾고자하는 쿼리의 개수를 알수있다.
context.fetch ( request ) 와 request.excute() 차이
- execute()는 context안에서 실행되어야한다고 코맨트되어있다.
Notification
- 데이터베이스안에서 변경사항들은 notification을 발생시킨다.
- 예외적으로 batch opertaion들을 사용하는경우에는 발생하지않는다.
- https://marcosantadev.com/core-data-notification-swift/
Fetch Result Controller
만약 리스트형식의 데이터를 보여주어야하고, 수정도 필요한경우에 이용한다.
- 굉장히 컬렉션뷰와 테이블뷰에 친화되어있다.
- 컬렉션뷰는 diffableDataSource와 snapShot을 이용해야 가능하다.
- FetchReuqest와 context를통해 인스턴스를 만들고, delegate를 구현한다.
- delegate 메소드들은 테이블뷰와 1-1로 매칭되도록 디자인되어있다.
batch operation
굉장히 많은 데이터들을 수정하거나, 삽입하거나, 삭제하는 경우에는 context.save 보다는 batch operation을 이용한다.
- 메모리사용도 줄어들고,
- notification을 발생시키지않기때문에 굉장히 빠르다.
- 그렇기때문에 변경사항들을 확인하고자한다면 Persistend History를 이용한다.
( 이렇게 대량의 Json들을 batch insert하는 방법 )
Persistent History
- 기본적으로 데이터베이스에 변경사항이 발생할경우 notification을 발생시키는데, 간혹 놓치거나, 아예 발생시키지않는경우에 변경사항들을 확인하고자 할때 사용한다.
- persistent container를 처음부터 persistent History를 사용한다고 선언한다. ( 세션참고하면 좋아요 )
- 특정 날짜나, token, 트랜잭션으로부터 History change Request를 생성하고, 변경사항들을 merge한다.
최적화관련
denormalization
만약 요약이 필요한데이터를 뽑고자할때, 모든데이터를 읽어와 전처리하는것은 굉장히비효율적이다.
그러므로 denormalization기법을 이용한다. ( 어려운 내용이아니에요 )
- 중복되는 데이터들을 추가적으로 저장하여 더 빠르게 검색하도록 위함이다.
- 이는 derived attribute를 이용한다면 아주 손쉽게 가능해진다. ( 세션보시면 바로 이해되실듯! )
'iOS' 카테고리의 다른 글
RxSwift, RxCocoa 정리 (0) | 2021.08.18 |
---|---|
CoreData와 CloudKit 연동하기 (2) | 2021.08.18 |
좌우 스크롤되는 캘린더뷰 만들기( feat: CompositionalLayout ) (0) | 2021.08.09 |
각 주제별 WWDC 참고용 (0) | 2021.07.09 |
WidgetKit 정리 (0) | 2021.07.07 |