-
iOS) Core Data (2) - Managed Object와 Managed Object ContextiOS 2021. 1. 26. 20:17
summary
- NSManagedObject
- NSManagedObjectContext
- Data Model 구성
- Entity 클래스 생성
Managed Object
Core Data는 모든 데이터를 Entity 단위로 처리한다. 여기서 Entity는 모델 관점의 용어이다.
context에서 처리하는 실제 데이터는 Managed Object이다. 여기서 Managed가 붙는 이유는 라이프 사이클을 core data가 관리 하기 때문이다.
Managed Object는 NSManagedObject 클래스로 구현 되어있다.
우리가 모델 파일에서 볼 수 있고 생성 할 수 있는 Entity는 swift에서 NSManagedObject와 1대1로 연결된다.
이 클래스는 관리 객체에 필요한 모든 기능이 구현되어 있다.
보통 subClassing을 통해 Entity Class를 생성한다.
Entity Class는 key-value 방법을 사용하지 않고 속성을 통해 attribute에 접근한다. 따라서 빠르고 간단하다.
Xcode는 데이터 모델을 기반으로 entity 클래스를 자동으로 생성한다.
Managed Object Context
Managed Object Context는 Core Data에서 가장 일을 많이하는 객체이다.
보통 context라고 불린다.
대부분의 작업은 context에서 처리되고 managed object는 항상 context안에 존재한다.
새로운 managed object를 생성 할때는 context를 지정해야 한다.
등록된 managed object는 context를 통해 persistent store에 저장되고, 이미 저장되어 있는 Managed Object를 수정하고 저장 할 때도 Managed Object를 복사하여 Context에 등록해야 한다.
Managed Object를 사용하기 위해선 반드시 Context에 등록이 필요하다.
XCode에서 Core Data Model 다루기
프로젝트 생성시 use Core Data를 선택하지 않는다면 Core Data Model이 존재 하지 않고 AppDelegate에도 CoreData관련 객체들이 존재 하지 않는다.
새로운 파일로 Core data Model을 선택 해준다.
Data Model을 만들고 선택하면 아래와 같은 화면이 나온다.
Person에 이름과 나이 속성을 추가한 모델이다.
아래의 AddEntity를 통해 Entity를 생성하고 Attribute란에 + 버튼을 사용하여 어트리뷰트를 생성 할 수 있다.
프로젝트 생성시 Use Core Data 항목을 체크하지 않았다면 Appdelegate에 Core Data관련 객체가 존재하지 않는다.
Core Data는 PersistentContainer라는 스택으로 관리되고 이 안에 Context, PersistentStore, Coordinator, ManagedObjectModel이 존재한다.
코드를 외우기는 어려우니 빈 프로젝트를 만들어서 붙혀넣자.
lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "CoredataEx") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }() // MARK: - Core Data Saving support func saveContext () { let context = persistentContainer.viewContext if context.hasChanges { do { try context.save() } catch { let nserror = error as NSError fatalError("Unresolved error \(nserror), \(nserror.userInfo)") } } }
코드를 보면 NSPersistentContainer(name: "CoredataEX")와 같이 Core Data 모델 이름으로 pesistentContainer를 초기화 하는 것이 보인다.
ViewController로 이동하여 우리가 사용할 Context를 생성하자.
import UIKit import CoreData class ViewController: UIViewController { var context: NSManagedObjectContext { guard let app = UIApplication.shared.delegate as? AppDelegate else { fatalError() } return app.persistentContainer.viewContext } }
유의해야 할 것은 반드시 CoreData 모듈을 import 시켜주어야 한다.
Core Data 객체는 UIKit의 서브클래스가 아니다.
AppDelegate에서 생성한 PersistentContainer에 존재하는 Context를 리턴해줬다.
자 이제 이 context를 통해서 데이터를 생성하고 저장하고 읽어오고 지지고 볶는데 단점이 존재한다.
새로운 데이터를 생성한다는 뜻은 데이터 모델에 맞는 형식의 데이터(Entity와 Attribute)를 생성해야 하고 아래와 같이 Set Value 형식을 사용 해야한다.
let newEntity = NSEntityDescription.insertNewObject(forEntityName: "Person", into: context) newEntity.setValue(name, forKeyPath: "name") newEntity.setValue(age, forKeyPath: "age")
string을 직접 입력해야 한다는 뜻은?? ----> '개발자의 실수를 야기한다'
또한 모델에 저장된 데이터를 가져와야 할 때 NSFetchRequest를 생성해야 하는데 이 또한 번거롭다.
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Person") do { let data = try context.fetch(request) ... } catch { print(error) }
때문에 가급적 NSManagedObject Class를 서브클래싱 하고 Attribute에 접근하는 Custom 방식을 추천한다.
Custom Entity Class
Core Data 모델로 돌아가서 Entity를 선택 후 Inspector를 보면 아래와 같은 속성이 나타난다.
Class 항목을 보면 Codegen 옵션이 존재한다.
Class Definition은 자동으로 생성되는 Entity 클래스를 사용 하는 것이고 Manual/None을 생성하면 Entity클래스를 커스텀 할 수 있다.
이후 Editor를 통해 Create NSManagedObject Subclass를 선택하면 Entity 클래스를 자동으로 생성 할 수 있다.
두개의 파일이 생성되었다.
PersonEntity 클래스가 생성 되었다.
이를 통해 KetValue 코딩이 아닌 직접 attribute에 접근 할 수 있다.
Data Manager 만들기
context를 사용하기 위해선 AppDelegate의 container를 각 VC에서 사용할 때마다 만들어 주어야한다.
이런 번거로움 때문에 하나의 singleton Class를 만들어서 stack을 관리하도록 하자.
import UIKit import CoreData class DataManager { static var shared = DataManager() private init() {} var container: NSPersistentContainer? var mainContext: NSManagedObjectContext { guard let context = container?.viewContext else { fatalError() } return context } func setup(modelName: String) { container = NSPersistentContainer(name: modelName) container?.loadPersistentStores(completionHandler: { (desc, error) in if let error = error { print(error) } }) } func saveMainContext() { mainContext.perform { if self.mainContext.hasChanges { do { try self.mainContext.save() } catch { print(error) } } } } }
AppDelegate 별 다를게 없는 DataManager 클래스이다.
다른 점은 mainContext를 가지고 있는 싱글톤 클래스 인것 뿐이다. 이제 context를 전역으로 사용 할 수 있게 되었다.
core data의 container는 보통 앱의 시작점에서 초기화 시키기 때문에 AppDelegate에서 어플리케이션이 시작 될 때 초기화를 해주면 사용 할 수 있다.
class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { DataManager.shared.setup(modelName: "Sample") return true } }
'iOS' 카테고리의 다른 글
iOS) Core Data (4) - Entity Hierarchy and Relationship (0) 2021.01.31 iOS) Core Data (3) - CRUD (0) 2021.01.27 iOS) Core Data (1) - over view (0) 2021.01.25 iOS) Custom Present 이용시 ViewContoller Life Cycle 문제 (1) 2021.01.06 iOS) Appstore Transition 따라하기(2) - Present Transition (2) 2021.01.05