Core Data의 createOrUpdate는 병합 정책에 따라 달라집니다.

9483 단어 iOSSwiftCoreData

소개



타이틀을 고민했지만 적절한 타이틀의 표현이 어렵다. Core Data에는 createOrUpdate는 존재하지 않지만 비슷한 것을 시도한 경우에 대해.

먼저 Constraints를 id로 설정하고 Constraints에 따라 context.save를 실행하는 경우를 가정합니다. 그 때 병합 정책에 의해 갱신되거나 되지 않는 것이 정해진다.

createOrUpdate 예제



name 속성으로 설정하지 않는 경우



병합 정책 NSMergeByPropertyObjectTrumpMergePolicy





병합 정책을 NSMergeByPropertyObjectTrumpMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
@objc(Person)
public class Person: NSManagedObject {
    @NSManaged public var id: Int32
    @NSManaged public var name: String?
}
func createPerson() {

    func debug(_ person: Person) {
        print("---")
        print("person.id:", person.id)
        print("person.name:", person.name ?? "nil")
    }

    let context = persistentContainer.viewContext
    context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // このポリシーによって変わる

    do {
        let person = Person(context: context)
        person.id = 1
        person.name = "p1"

        saveContext()

        let request = NSFetchRequest<Person>(entityName: "Person")

        let entities = try! context.fetch(request)
        debug(entities.first!)
    }

    do {
        let person = Person(context: context)
        person.id = 1
//            person.name = "p1" // ここをセットしない

        saveContext()

        let request = NSFetchRequest<Person>(entityName: "Person")

        let entities = try! context.fetch(request)
        debug(entities.first!)
    }
}

// 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)")
        }
    }
}

출력은 아래와 같다. name 을 세트 하지 않으면 nil 에 의해 덧쓰기된다 (또는 새로운 오브젝트가 id: 1 로서 덧쓰기된다).
---
person.id: 1
person.name: p1
---
person.id: 1
person.name: nil

병합 정책 NSMergeByPropertyStoreTrumpMergePolicy



병합 정책을 NSMergeByPropertyStoreTrumpMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
---
person.id: 1
person.name: p1
---
person.id: 1
person.name: p1

아마도 영속화된 데이터와 id에 의한 충돌이 일어나 병합이 일어나고 있다. 그 때 Store를 우선하기 위해 p1을 덮어 쓰지 않는다.

Store를 우선하기 위하여 name가 다시 쓰여지면 이미 p1이 아닐 수도 있다.

병합 정책이 NSErrorMergePolicy인 경우



병합 정책을 NSErrorMergePolicy로 전환하고 DB를 지운 결과는 다음과 같습니다.
---
person.id: 1
person.name: p1
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=133021 "(null)" UserInfo={NSExceptionOmitCallstacks=true, conflictList=(
    "NSConstraintConflict (0x600000af8b80) for constraint (\n    id\n): database: 0xf09c831374aa7f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p1>, conflictedObjects: (\n    \"0xf09c831374a67f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p2>\"\n)"
)}, ["NSExceptionOmitCallstacks": 1, "conflictList": <__NSArrayM 0x6000011ef3c0>(
NSConstraintConflict (0x600000af8b80) for constraint (
    id
): database: <Person: 0x600003c84d20> (entity: Person; id: 0xf09c831374aa7f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p1>; data: {
    id = 1;
    name = p1;
}), conflictedObjects: (
    "<Person: 0x600003c9c730> (entity: Person; id: 0xf09c831374a67f8e <x-coredata://135BBA78-677D-4C61-B065-CD5DF8EB0DB5/Person/p2>; data: {\n    id = 1;\n    name = nil;\n})"
)
)
]

처음에는 작성되었지만 다음에 같은 id로 갱신하려고 하면 충돌이 일어나 병합이 실패한다.

결론



Core Data의 충돌은 복수 context의 동시 병합에 기인하는 것이 아니라, 이미 영속화되고 있는 Store와 온메모리의 데이터의 불일치에 의해 충돌이 일어나 발생한다. 동시 병합에서도 결국 충돌 발생 위험은 높아지지만 그것은 계기의 하나 밖에 없다. 충돌시 병합 정책에 따라 행동이 결정된다는 것입니다.

병합 정책의 기본값은 NSErrorMergePolicy이기 때문에 상대적으로 실수를 알기 쉽습니다.

관련



Realm은 어떻게 될 것인가?

좋은 웹페이지 즐겨찾기