나만의 공부 노트

Core Data에 관하여 본문

정리 X/Core Services

Core Data에 관하여

va95 2021. 2. 6. 03:03

#Core Data

(데이터 베이스 공부해야할 듯..!)

코어 데이터 != 데이터 베이스 가 아님 (framework이고 물론 persistent를 지원하지만 다른 기능도 많음! 그중에 일부 일 뿐)

 

모든 entity는 NSManagedObject라는 클래스를 상속받고 있음

데이터베이스 자체는 NSManagedObjectContext이고 얘를 통해서 데이터베이스안의 데이터에 접근 시작

-> Context는 transaction일 뿐!

-> persistentcontainer가 데이터베이스, transaction, model을 다 들고 있음 ㅎ

 

 


 

 

 

1.

let container = (UIApplication.shared.delegate as! AppDelegate).persistentContainer   <-   NSPersistentContainer

접근은 이렇게!

-> UIApplication이라는 클래스에 static이라는 변수로 shared가 있음

shared에 유일한 앱 참조값을 넣어주는 듯(싱글턴?)

 

2.

let context: NSManagedObjectContext = container.viewContext <- 얘는 메인 스레드에서만 실행!!!!!!!!!!!!!!!!!!!!!! 매우 중요!!!!!!!!!!!!!!!!(view와도 관련이 있기 때문에) -> 쓰레드 안전형이 아님

but 멀티 쓰레딩으로 하는 법을 나중에 배워보자 // 아하 데이터 베이스를 동시에 접근하면 문제가 발생하니깐 메인에서만 다루는게 편하긴 하겠다. 다만 성능이..

-> context는 만들어진 queue에서만 사용이 가능하다!(context들끼리 데이터베이스를 안전하게 제어하기 위해서 스스로 조율한다)

 

3.

시작하기에 앞서 매번  (UIApplication.shared.delegate as! AppDelegate).persistentContainer 요렇게 쓰는 거 보다

class AppDelegate {

    static var persistentContainer: NSPersistentContainer {

        return (UIApplication.shared.delegate as! AppDelegate).persistentContainer

    } <- 계산 프로퍼티

}

이렇게 만들면 AppDelegate.persistentContainer로 손쉽게 접근 가능

(강의에선 static 계산 변수명과 저장 프로퍼티 변수명이 똑같은데 이게 되나? 구분이 되나?)

-> 오 되네 근데 static 변수에 접근하려면 클래스명 붙여야됨. 아니면 일반 프로퍼티 우선인듯

 

class AppDelegate {

    static var viewContext: NSManagedObjectContext {

        return persistentContainer.viewContext

    } <- 계산 프로퍼티

}

얘도 이렇게 써랑

 

-> 데이터베이스가 여러개라면 이제 persistentContainer 여러개로 만들면 된다!

-> 근데 대부분의 앱은 거대한 데이터베이스 하나만 갖고 있는 구조라고 함.. 크게 걱정은 할 필요 없을듯

 

4.

이제 예시를 봐보자

import CoreData <- core service 계층( 1. core os 2. core service 3. media 4. cocoa pod  )

 

let context = AppDelegate.viewContext

let tweet: NSManagedObject = NSEntityDescription.insertNewObject(forEntityName: "Tweet", into: context)

-> tweet에 엔티티를 가리키는 주소값이 들어감

-> tweet 변수에 값을 넣어주기만 하면 됨

-> 방법은 아래..

 

func value(forKey: String) -> Any?

func setValue(Any?, forKey: String)

 

저장하기

tweet.setValue( 아이템 , forKeyPath: "tweetwer.name")

 

꺼내오기

-> let username = tweet.value(forKeyPath: "tweetwer.name") as? String

 

 

근데 이 방법은 매우 지저분한 API 임과 동시에 타입이 Any이기 때문에 타입 검사가 안됨, 나중에 실행시에 타입 오류 가능성 다분(실수 안하면 되긴하는데.. 불가능하겠지..)

-> 모델에서 만들어준 type과 달라도 any이기 때문에 걍 타입 검사 없이 다 받아들임

-> 나중에 실행해보면 타입 다를때 에러 낼거임

그래서 뒤에 방법 사용

 

5.

Codegen을 이용하면 자동으로 entity와 그 안에 값들을 -> class 마냥 코드로 만들어줌(오우!)

codegen을 통해 none, class, extension 3가지 중 하나를 쓸 수 있는데

* none은 4번 방법처럼

* class는 자동으로 class를 만들어줌(<- 자동으로 class를 만들기 때문에 네비게이터에 안나오고, 변수만 손쉽게 접근하는 방법인듯?, 메서드 만들기 불가능한듯? 나중에 확인해봄세 머리 아픔) -> 맞음 추가 구현 안하면 얘 쓰면 됨

* extension은 class로 만들어놓으면 자동으로 extension으로 프로퍼티 붙여줌 + 기본 추가,삭제 메서드 넣어줌(extension은 저장 프로퍼티 안된다며!? -> @NS어쩌구.. 덕분에  이게 보이면 core data가 튀어나와서 내가 해결해줌! 하고 나타난다고 함..)

-> 얘가 장점이 변수 접근도 되면서 클래스에 추가 기능 구현하기 좋음 얠 자주 쓴다고 함(클래스 구현해야겠지? 안하면 바보)

 

class나 extension으로 만들면 프로퍼티 타입을 정확히 아니깐 타입 검사도 가능해져서 빌드 전에 자동으로 에러 내줌

매우 굿

if let tweet = Tweet(context: context) {

    tweet.text = "~~~~~~"

    tweet.created = Date()       <-     만약 여기에 실수로 String을 넣었다? 4번 방법이였으면 에러 안내고 넘어가버림

    let joe = TwitterUser(context: tweet.managedObjectContext)

    tweet.tweeter = joe                                                <- 자동으로 반대편도 업데이트됨!

    tweet.tweeter.name = "Joe Schmo"

} <- tweeter는 relation 

 

if let joesTweets = joe.tweets as? Set<Tweet>  {

    if joesTweets.contains(tweet) { print("yes") }

} <- 확인해보면 자동으로 업데이트 된걸 볼 수 있음

 

 

 

<- 1대 다수 의 관계를 맺고 있다면 기본적으로 NSSet? 타입임 -> 타입 캐스팅을 통해 Set<Tweet>으로 변환해서 사용 가능욤

<- joe라는 변수가 없다면 어떻게 joe를 얻어오지?? 나중에 나오겠지..? 일단 패스 -> querying을 통해 얻어온다! 다음 문서에 정리해놨따

이상한 점 : let joe = TwitterUser(context: tweet.managedObjectContext)

왜 여기서 let joe = TwitterUser(context: AppDelegate.context) 가 아니냐? 하면

tweet가 들어가있는 데이터베이스 안에 tweeter를 넣어주고 싶기때문에 가장 안전하게 하는 방법이기 때문! <- 패러다임이라고 함, 그냥 확실히 하기 위해서

context가 여러개인 상황에서 실수로 다른 context에 넣어주는 현상 방지하자

 

 

 

* 데이터 모델 편집기에서 값들의 속성 탭을 보면 integer16, 32 같은 타입에 Use Scalar Type 이라는 옵션이 있음

보통 데이터 베이스에 interger, double이 아닌 NSNumber 타입으로 바꾸어서 넣어주었다고 함

NSNumber로 바꾸지 말고 바로 원시 타입 그대로 넣고 싶다면 해당 옵션을 체크할 것

숫자 쓸땐 얘를 기본적으로 켜자!

 

 

6.

데이터를 삭제해볼까?

managedObjectedContext.delete(_ object: tweet)

사용법이 매우 깔끔!

삭제 규칙을 따름

 

func prepareForDeletion() {

    // 삭제 전에 특정 행동이 필요할 때 유용

    // 얘를 들어 tweet을 삭제하면 tweeter의 tweet count가 1 감소 해야하는데

    // tweet의 관계를 통해 해당 tweerter로 접근해서 count 깎으면 됨

}

 

 

 

 

7.

앱이 종료되면 저장을 해보자

do {

    try context.save()

} catch {

    // 에러 관리 부분

}

 

2탄 커밍쑨..

'정리 X > Core Services' 카테고리의 다른 글

Core Date(thread)에 관하여  (0) 2021.02.06
Core Data(query)에 관하여  (0) 2021.02.06