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 { switch child.value {
case let property as FieldAttributeProtocol: 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( attributes[property.keyPath] = type(of: property).read(
field: property, field: property,
for: object.rawObject!, for: object.rawObject!
bypassThreadCheck: false
) )
case let property as FieldRelationshipProtocol: 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( attributes[property.keyPath] = type(of: property).valueForSnapshot(
field: property, field: property,
for: object.rawObject! for: object.rawObject!
@@ -219,10 +226,23 @@ extension CoreStoreObject {
switch property { switch property {
case let property as FieldAttributeProtocol: 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( values[property.keyPath] = type(of: property).read(
field: property, field: property,
for: rawObject, for: rawObject
bypassThreadCheck: false )
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: default:

View File

@@ -47,7 +47,7 @@ public protocol FieldRelationshipType {
static func cs_toSnapshotType(from value: PublishedType) -> SnapshotValueType 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 {} public protocol FieldRelationshipToOneType: FieldRelationshipType {}
@@ -88,9 +88,9 @@ extension Optional: FieldRelationshipType, FieldRelationshipToOneType where Wrap
return value?.objectID() 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() }) 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 objectIDs
return []
}
return value.map({ ($0 as! NSManagedObject).objectID })
} }
} }
@@ -173,12 +169,8 @@ extension Set: FieldRelationshipType, FieldRelationshipToManyType, FieldRelation
return SnapshotValueType(value.map({ $0.objectID() })) 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 .init(objectIDs)
return []
}
return .init(value.map({ ($0 as! NSManagedObject).objectID }))
} }
} }

View File

@@ -161,7 +161,11 @@ extension FieldContainer {
instance.rawObject != nil, 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." "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 { set {
@@ -169,6 +173,10 @@ extension FieldContainer {
instance.rawObject != nil, 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." "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) return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
} }
} }
@@ -200,12 +208,13 @@ extension FieldContainer {
// MARK: FieldProtocol // 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 field = field as! Self
if let customGetter = field.customGetter { if let customGetter = field.customGetter {
@@ -227,10 +236,6 @@ extension FieldContainer {
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) { 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( Internals.assert(
rawObject.isEditableInContext() == true, rawObject.isEditableInContext() == true,
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction." "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, 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." "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 { set {
@@ -107,6 +111,10 @@ extension FieldContainer {
instance.rawObject != nil, 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." "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) return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
} }
} }
@@ -138,12 +146,13 @@ extension FieldContainer {
// MARK: FieldProtocol // 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 field = field as! Self
let keyPath = field.keyPath let keyPath = field.keyPath
return V.cs_toReturnType( return V.cs_toReturnType(
@@ -152,11 +161,7 @@ extension FieldContainer {
} }
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) { 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( Internals.assert(
rawObject.isEditableInContext() == true, rawObject.isEditableInContext() == true,
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction." "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." "Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
) )
let field = field as! Self let field = field as! Self
let keyPath = field.keyPath return V.cs_valueForSnapshot(from: rawObject.objectIDs(forRelationshipNamed: field.keyPath))
return V.cs_valueForSnapshot(
from: rawObject.value(forKey: keyPath) as! V.NativeValueType?
)
} }

View File

@@ -128,7 +128,11 @@ extension FieldContainer {
instance.rawObject != nil, 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." "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 { set {
@@ -136,6 +140,10 @@ extension FieldContainer {
instance.rawObject != nil, 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." "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) return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
} }
} }
@@ -167,12 +175,13 @@ extension FieldContainer {
// MARK: FieldProtocol // 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 field = field as! Self
if let customGetter = field.customGetter { if let customGetter = field.customGetter {
@@ -194,10 +203,6 @@ extension FieldContainer {
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) { 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( Internals.assert(
rawObject.isEditableInContext() == true, rawObject.isEditableInContext() == true,
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction." "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, 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." "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 { set {
@@ -147,6 +151,10 @@ extension FieldContainer {
instance.rawObject != nil, 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." "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) return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
} }
} }
@@ -178,12 +186,13 @@ extension FieldContainer {
// MARK: FieldProtocol // 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 field = field as! Self
if let customGetter = field.customGetter { if let customGetter = field.customGetter {
@@ -205,10 +214,6 @@ extension FieldContainer {
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) { 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( Internals.assert(
rawObject.isEditableInContext() == true, rawObject.isEditableInContext() == true,
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction." "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? defaultValue: Any?
) )
static var dynamicObjectType: CoreStoreObject.Type { get }
var entityDescriptionValues: () -> EntityDescriptionValues { get } var entityDescriptionValues: () -> EntityDescriptionValues { get }
var getter: CoreStoreManagedObject.CustomGetter? { get } var getter: CoreStoreManagedObject.CustomGetter? { get }
var setter: CoreStoreManagedObject.CustomSetter? { get } var setter: CoreStoreManagedObject.CustomSetter? { get }

View File

@@ -30,7 +30,9 @@ import CoreData
// MARK: - FieldProtocol // MARK: - FieldProtocol
internal protocol FieldProtocol: PropertyProtocol { 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?) 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 let keyPathString = field.keyPath
self.getValue = { self.getValue = {
return type(of: field).read( return type(of: field).read(field: field, for: rawObject) as! V
field: field,
for: rawObject,
bypassThreadCheck: true // May be called from NSError logs
) as! V
} }
self.setValue = { self.setValue = {
@@ -163,11 +159,7 @@ public struct ObjectProxy<O: CoreStoreObject> {
let keyPathString = field.keyPath let keyPathString = field.keyPath
self.getValue = { self.getValue = {
return type(of: field).read( return type(of: field).read(field: field, for: rawObject) as! V
field: field,
for: rawObject,
bypassThreadCheck: true // May be called from NSError logs
) as! V
} }
self.setValue = { self.setValue = {
@@ -200,11 +192,7 @@ public struct ObjectProxy<O: CoreStoreObject> {
let keyPathString = field.keyPath let keyPathString = field.keyPath
self.getValue = { self.getValue = {
return type(of: field).read( return type(of: field).read(field: field, for: rawObject) as! V
field: field,
for: rawObject,
bypassThreadCheck: true // May be called from NSError logs
) as! V
} }
self.setValue = { self.setValue = {

View File

@@ -379,8 +379,12 @@ extension ObjectPublisher where O: CoreStoreObject {
return nil 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] 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 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] 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 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] 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 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] let field = object[keyPath: member]
guard let value = type(of: field).read(field: field, for: rawObject, bypassThreadCheck: false) as! V? else { let snapshotValue = V.cs_valueForSnapshot(from: rawObject.objectIDs(forRelationshipNamed: field.keyPath))
return nil
}
let nativeValue = V.cs_toNativeType(from: value)
let snapshotValue = V.cs_valueForSnapshot(from: nativeValue)
return V.cs_toPublishedType(from: snapshotValue, in: self.context) return V.cs_toPublishedType(from: snapshotValue, in: self.context)
} }