check correct queue for managed object value access

This commit is contained in:
John Estropia
2017-04-12 19:22:18 +09:00
parent 9f3db61ff7
commit a73306fecb
10 changed files with 239 additions and 40 deletions

View File

@@ -31,23 +31,16 @@ import Foundation
public final class CoreStoreSchema: DynamicSchema {
public convenience init(modelVersion: String, _ entity: DynamicEntity, _ entities: DynamicEntity...) {
public convenience init(modelVersion: String, entities: [DynamicEntity], versionLock: VersionLock? = nil) {
self.init(
modelVersion: modelVersion,
entities: [entity] + entities
entitiesByConfiguration: [DataStack.defaultConfigurationName: entities],
versionLock: versionLock
)
}
public convenience init(modelVersion: String, entities: [DynamicEntity]) {
self.init(
modelVersion: modelVersion,
entitiesByConfiguration: [DataStack.defaultConfigurationName: entities]
)
}
public required init(modelVersion: String, entitiesByConfiguration: [String: [DynamicEntity]]) {
public required init(modelVersion: String, entitiesByConfiguration: [String: [DynamicEntity]], versionLock: VersionLock? = nil) {
var actualEntitiesByConfiguration: [String: Set<AnyEntity>] = [:]
for (configuration, entities) in entitiesByConfiguration {
@@ -58,7 +51,7 @@ public final class CoreStoreSchema: DynamicSchema {
actualEntitiesByConfiguration[DataStack.defaultConfigurationName] = allEntities
CoreStore.assert(
autoreleasepool {
cs_lazy {
let expectedCount = allEntities.count
return Set(allEntities.map({ ObjectIdentifier($0.type) })).count == expectedCount
@@ -70,6 +63,23 @@ public final class CoreStoreSchema: DynamicSchema {
self.modelVersion = modelVersion
self.entitiesByConfiguration = actualEntitiesByConfiguration
self.allEntities = allEntities
if let versionLock = versionLock {
CoreStore.assert(
versionLock == VersionLock(entityVersionHashesByName: self.rawModel().entityVersionHashesByName),
"A \(cs_typeName(VersionLock.self)) was provided for the \(cs_typeName(CoreStoreSchema.self)) with version \"\(modelVersion)\", but the actual hashes do not match. This may result in unwanted migrations or unusable persistent stores.\nExpected lock values: \(versionLock)\nActual lock values: \(VersionLock(entityVersionHashesByName: self.rawModel().entityVersionHashesByName))"
)
}
else {
#if DEBUG
CoreStore.log(
.notice,
message: "These are hashes for the \(cs_typeName(CoreStoreSchema.self)) with version name \"\(modelVersion)\". Copy the dictionary below and pass it to the \(cs_typeName(CoreStoreSchema.self)) initializer's \"versionLock\" argument:\n\(VersionLock(entityVersionHashesByName: self.rawModel().entityVersionHashesByName))"
)
#endif
}
}

View File

@@ -43,8 +43,7 @@ public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
public init(_ entityName: String) {
self.type = O.self
self.entityName = entityName
self.init(O.self, entityName)
}
public init(_ type: O.Type, _ entityName: String) {
@@ -54,29 +53,6 @@ public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
}
// MARK: - VersionHash
public struct VersionHash: ExpressibleByArrayLiteral {
let hash: Data
public init(_ hash: Data) {
self.hash = hash
}
// MARK: ExpressibleByArrayLiteral
public typealias Element = UInt8
public init(arrayLiteral elements: UInt8...) {
self.hash = Data(bytes: elements)
}
}
// MARK: DynamicEntity
public let type: CoreStoreObject.Type

View File

@@ -84,6 +84,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
get {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.accessRawObject()
.getValue(
forKvcKey: self.keyPath,
@@ -92,6 +96,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
}
set {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
self.accessRawObject()
.setValue(
newValue,
@@ -175,6 +183,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
get {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.accessRawObject()
.getValue(
forKvcKey: self.keyPath,
@@ -190,6 +202,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
}
set {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
self.accessRawObject()
.setValue(
newValue,
@@ -279,6 +295,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
get {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.accessRawObject()
.getValue(
forKvcKey: self.keyPath,
@@ -294,6 +314,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
}
set {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
self.accessRawObject()
.setValue(
newValue,

View File

@@ -31,6 +31,9 @@ import Foundation
public final class SchemaHistory: ExpressibleByArrayLiteral {
// MARK: -
public let currentModelVersion: ModelVersion
public let migrationChain: MigrationChain
@@ -157,6 +160,7 @@ public final class SchemaHistory: ExpressibleByArrayLiteral {
CoreStore.abort("Could not resolve the \(cs_typeName(SchemaHistory.self)) current model version because the \(cs_typeName(MigrationChain.self)) have ambiguous leaf versions: \(candidateVersions)")
}
}
self.schemaByVersion = schemaByVersion
self.migrationChain = migrationChain
self.currentModelVersion = currentModelVersion

View File

@@ -70,6 +70,10 @@ public enum ValueContainer<O: CoreStoreObject> {
get {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.accessRawObject()
.getValue(
forKvcKey: self.keyPath,
@@ -78,6 +82,10 @@ public enum ValueContainer<O: CoreStoreObject> {
}
set {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
self.accessRawObject()
.setValue(
newValue,
@@ -139,6 +147,10 @@ public enum ValueContainer<O: CoreStoreObject> {
get {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
return self.accessRawObject()
.getValue(
forKvcKey: self.keyPath,
@@ -147,6 +159,10 @@ public enum ValueContainer<O: CoreStoreObject> {
}
set {
CoreStore.assert(
self.accessRawObject().isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
self.accessRawObject()
.setValue(
newValue,

View File

@@ -0,0 +1,89 @@
//
// VersionLock.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
// MARK: - VersionLock
public struct VersionLock: ExpressibleByDictionaryLiteral, Equatable {
public typealias HashElement = UInt64
public let hashesByEntityName: [EntityName: Data]
public init(_ intArrayByEntityName: [EntityName: [HashElement]]) {
self.init(keyValues: intArrayByEntityName.map({ $0 }))
}
// MARK: ExpressibleByDictionaryLiteral
public typealias Key = EntityName
public typealias Value = [HashElement]
public init(dictionaryLiteral elements: (EntityName, [HashElement])...) {
self.init(keyValues: elements)
}
// MARK: Equatable
public static func == (lhs: VersionLock, rhs: VersionLock) -> Bool {
return lhs.hashesByEntityName == rhs.hashesByEntityName
}
// MARK: Internal
internal init(entityVersionHashesByName: [String: Data]) {
self.hashesByEntityName = entityVersionHashesByName
}
// MARK: Private
private init(keyValues: [(EntityName, [HashElement])]) {
var hashesByEntityName: [EntityName: Data] = [:]
for (entityName, intArray) in keyValues {
hashesByEntityName[entityName] = Data(
buffer: UnsafeBufferPointer(
start: intArray,
count: intArray.count
)
)
}
self.hashesByEntityName = hashesByEntityName
}
}