performant access of relationship objectIDs for snapshots

This commit is contained in:
John Estropia
2020-02-21 13:51:17 +09:00
parent 361dba58c6
commit 231e138ab0
10 changed files with 121 additions and 89 deletions

View File

@@ -172,13 +172,20 @@ extension CoreStoreObject {
switch child.value {
case let property as FieldAttributeProtocol:
Internals.assert(
object.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(type(of: property).dynamicObjectType))'s value outside it's designated queue."
)
attributes[property.keyPath] = type(of: property).read(
field: property,
for: object.rawObject!,
bypassThreadCheck: false
for: object.rawObject!
)
case let property as FieldRelationshipProtocol:
Internals.assert(
object.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(type(of: property).dynamicObjectType))'s value outside it's designated queue."
)
attributes[property.keyPath] = type(of: property).valueForSnapshot(
field: property,
for: object.rawObject!
@@ -219,10 +226,23 @@ extension CoreStoreObject {
switch property {
case let property as FieldAttributeProtocol:
Internals.assert(
object.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(type(of: property).dynamicObjectType))'s value outside it's designated queue."
)
values[property.keyPath] = type(of: property).read(
field: property,
for: rawObject,
bypassThreadCheck: false
for: rawObject
)
case let property as FieldRelationshipProtocol:
Internals.assert(
object.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(type(of: property).dynamicObjectType))'s value outside it's designated queue."
)
values[property.keyPath] = type(of: property).valueForSnapshot(
field: property,
for: object.rawObject!
)
default:

View File

@@ -47,7 +47,7 @@ public protocol FieldRelationshipType {
static func cs_toSnapshotType(from value: PublishedType) -> SnapshotValueType
static func cs_valueForSnapshot(from value: NativeValueType?) -> SnapshotValueType
static func cs_valueForSnapshot(from objectIDs: [DestinationObjectType.ObjectID]) -> SnapshotValueType
}
public protocol FieldRelationshipToOneType: FieldRelationshipType {}
@@ -88,9 +88,9 @@ extension Optional: FieldRelationshipType, FieldRelationshipToOneType where Wrap
return value?.objectID()
}
public static func cs_valueForSnapshot(from value: NativeValueType?) -> SnapshotValueType {
public static func cs_valueForSnapshot(from objectIDs: [DestinationObjectType.ObjectID]) -> SnapshotValueType {
return value?.objectID
return objectIDs.first
}
}
@@ -129,13 +129,9 @@ extension Array: FieldRelationshipType, FieldRelationshipToManyType, FieldRelati
return value.map({ $0.objectID() })
}
public static func cs_valueForSnapshot(from value: NativeValueType?) -> SnapshotValueType {
public static func cs_valueForSnapshot(from objectIDs: [DestinationObjectType.ObjectID]) -> SnapshotValueType {
guard let value = value else {
return []
}
return value.map({ ($0 as! NSManagedObject).objectID })
return objectIDs
}
}
@@ -173,12 +169,8 @@ extension Set: FieldRelationshipType, FieldRelationshipToManyType, FieldRelation
return SnapshotValueType(value.map({ $0.objectID() }))
}
public static func cs_valueForSnapshot(from value: NativeValueType?) -> SnapshotValueType {
public static func cs_valueForSnapshot(from objectIDs: [DestinationObjectType.ObjectID]) -> SnapshotValueType {
guard let value = value else {
return []
}
return .init(value.map({ ($0 as! NSManagedObject).objectID }))
return .init(objectIDs)
}
}

View File

@@ -161,7 +161,11 @@ extension FieldContainer {
instance.rawObject != nil,
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, bypassThreadCheck: false) as! V
Internals.assert(
instance.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!) as! V
}
set {
@@ -169,6 +173,10 @@ extension FieldContainer {
instance.rawObject != nil,
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
Internals.assert(
instance.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
}
}
@@ -200,12 +208,13 @@ extension FieldContainer {
// MARK: FieldProtocol
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject, bypassThreadCheck: Bool) -> Any? {
internal static var dynamicObjectType: CoreStoreObject.Type {
return ObjectType.self
}
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject) -> Any? {
Internals.assert(
bypassThreadCheck || rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = field as! Self
if let customGetter = field.customGetter {
@@ -227,10 +236,6 @@ extension FieldContainer {
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) {
Internals.assert(
rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
Internals.assert(
rawObject.isEditableInContext() == true,
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction."

View File

@@ -99,7 +99,11 @@ extension FieldContainer {
instance.rawObject != nil,
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, bypassThreadCheck: false) as! V
Internals.assert(
instance.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!) as! V
}
set {
@@ -107,6 +111,10 @@ extension FieldContainer {
instance.rawObject != nil,
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
Internals.assert(
instance.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
}
}
@@ -138,12 +146,13 @@ extension FieldContainer {
// MARK: FieldProtocol
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject, bypassThreadCheck: Bool) -> Any? {
internal static var dynamicObjectType: CoreStoreObject.Type {
return ObjectType.self
}
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject) -> Any? {
Internals.assert(
bypassThreadCheck || rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = field as! Self
let keyPath = field.keyPath
return V.cs_toReturnType(
@@ -152,11 +161,7 @@ extension FieldContainer {
}
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) {
Internals.assert(
rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
Internals.assert(
rawObject.isEditableInContext() == true,
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction."
@@ -182,10 +187,7 @@ extension FieldContainer {
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = field as! Self
let keyPath = field.keyPath
return V.cs_valueForSnapshot(
from: rawObject.value(forKey: keyPath) as! V.NativeValueType?
)
return V.cs_valueForSnapshot(from: rawObject.objectIDs(forRelationshipNamed: field.keyPath))
}

View File

@@ -128,7 +128,11 @@ extension FieldContainer {
instance.rawObject != nil,
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, bypassThreadCheck: false) as! V
Internals.assert(
instance.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!) as! V
}
set {
@@ -136,6 +140,10 @@ extension FieldContainer {
instance.rawObject != nil,
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
Internals.assert(
instance.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
}
}
@@ -167,12 +175,13 @@ extension FieldContainer {
// MARK: FieldProtocol
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject, bypassThreadCheck: Bool) -> Any? {
internal static var dynamicObjectType: CoreStoreObject.Type {
return ObjectType.self
}
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject) -> Any? {
Internals.assert(
bypassThreadCheck || rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = field as! Self
if let customGetter = field.customGetter {
@@ -194,10 +203,6 @@ extension FieldContainer {
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) {
Internals.assert(
rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
Internals.assert(
rawObject.isEditableInContext() == true,
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction."

View File

@@ -139,7 +139,11 @@ extension FieldContainer {
instance.rawObject != nil,
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, bypassThreadCheck: false) as! V
Internals.assert(
instance.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!) as! V
}
set {
@@ -147,6 +151,10 @@ extension FieldContainer {
instance.rawObject != nil,
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
)
Internals.assert(
instance.rawObject?.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
}
}
@@ -178,12 +186,13 @@ extension FieldContainer {
// MARK: FieldProtocol
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject, bypassThreadCheck: Bool) -> Any? {
internal static var dynamicObjectType: CoreStoreObject.Type {
return ObjectType.self
}
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject) -> Any? {
Internals.assert(
bypassThreadCheck || rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = field as! Self
if let customGetter = field.customGetter {
@@ -205,10 +214,6 @@ extension FieldContainer {
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) {
Internals.assert(
rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
Internals.assert(
rawObject.isEditableInContext() == true,
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction."

View File

@@ -43,6 +43,8 @@ internal protocol FieldAttributeProtocol: FieldProtocol {
defaultValue: Any?
)
static var dynamicObjectType: CoreStoreObject.Type { get }
var entityDescriptionValues: () -> EntityDescriptionValues { get }
var getter: CoreStoreManagedObject.CustomGetter? { get }
var setter: CoreStoreManagedObject.CustomSetter? { get }

View File

@@ -30,7 +30,9 @@ import CoreData
// MARK: - FieldProtocol
internal protocol FieldProtocol: PropertyProtocol {
static var dynamicObjectType: CoreStoreObject.Type { get }
static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject, bypassThreadCheck: Bool) -> Any?
static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject) -> Any?
static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?)
}

View File

@@ -133,11 +133,7 @@ public struct ObjectProxy<O: CoreStoreObject> {
let keyPathString = field.keyPath
self.getValue = {
return type(of: field).read(
field: field,
for: rawObject,
bypassThreadCheck: true // May be called from NSError logs
) as! V
return type(of: field).read(field: field, for: rawObject) as! V
}
self.setValue = {
@@ -163,11 +159,7 @@ public struct ObjectProxy<O: CoreStoreObject> {
let keyPathString = field.keyPath
self.getValue = {
return type(of: field).read(
field: field,
for: rawObject,
bypassThreadCheck: true // May be called from NSError logs
) as! V
return type(of: field).read(field: field, for: rawObject) as! V
}
self.setValue = {
@@ -200,11 +192,7 @@ public struct ObjectProxy<O: CoreStoreObject> {
let keyPathString = field.keyPath
self.getValue = {
return type(of: field).read(
field: field,
for: rawObject,
bypassThreadCheck: true // May be called from NSError logs
) as! V
return type(of: field).read(field: field, for: rawObject) as! V
}
self.setValue = {

View File

@@ -379,8 +379,12 @@ extension ObjectPublisher where O: CoreStoreObject {
return nil
}
Internals.assert(
rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = object[keyPath: member]
return type(of: field).read(field: field, for: rawObject, bypassThreadCheck: false) as! V?
return type(of: field).read(field: field, for: rawObject) as! V?
}
/**
@@ -395,8 +399,12 @@ extension ObjectPublisher where O: CoreStoreObject {
return nil
}
Internals.assert(
rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = object[keyPath: member]
return type(of: field).read(field: field, for: rawObject, bypassThreadCheck: false) as! V?
return type(of: field).read(field: field, for: rawObject) as! V?
}
/**
@@ -411,8 +419,12 @@ extension ObjectPublisher where O: CoreStoreObject {
return nil
}
Internals.assert(
rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = object[keyPath: member]
return type(of: field).read(field: field, for: rawObject, bypassThreadCheck: false) as! V?
return type(of: field).read(field: field, for: rawObject) as! V?
}
/**
@@ -427,13 +439,12 @@ extension ObjectPublisher where O: CoreStoreObject {
return nil
}
Internals.assert(
rawObject.isRunningInAllowedQueue() == true,
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
)
let field = object[keyPath: member]
guard let value = type(of: field).read(field: field, for: rawObject, bypassThreadCheck: false) as! V? else {
return nil
}
let nativeValue = V.cs_toNativeType(from: value)
let snapshotValue = V.cs_valueForSnapshot(from: nativeValue)
let snapshotValue = V.cs_valueForSnapshot(from: rawObject.objectIDs(forRelationshipNamed: field.keyPath))
return V.cs_toPublishedType(from: snapshotValue, in: self.context)
}