From 231e138ab05776944f0aaf601ff9e7155bcdfa8b Mon Sep 17 00:00:00 2001 From: John Estropia Date: Fri, 21 Feb 2020 13:51:17 +0900 Subject: [PATCH] performant access of relationship objectIDs for snapshots --- Sources/DynamicObject.swift | 28 ++++++++++++++++++++---- Sources/FIeldRelationshipType.swift | 22 ++++++------------- Sources/Field.Coded.swift | 25 +++++++++++++--------- Sources/Field.Relationship.swift | 32 +++++++++++++++------------- Sources/Field.Stored.swift | 25 +++++++++++++--------- Sources/Field.Virtual.swift | 25 +++++++++++++--------- Sources/FieldAttributeProtocol.swift | 2 ++ Sources/FieldProtocol.swift | 4 +++- Sources/ObjectProxy.swift | 18 +++------------- Sources/ObjectPublisher.swift | 29 +++++++++++++++++-------- 10 files changed, 121 insertions(+), 89 deletions(-) diff --git a/Sources/DynamicObject.swift b/Sources/DynamicObject.swift index 3f09383..21060df 100644 --- a/Sources/DynamicObject.swift +++ b/Sources/DynamicObject.swift @@ -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: diff --git a/Sources/FIeldRelationshipType.swift b/Sources/FIeldRelationshipType.swift index b19e751..9272af6 100644 --- a/Sources/FIeldRelationshipType.swift +++ b/Sources/FIeldRelationshipType.swift @@ -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) } } diff --git a/Sources/Field.Coded.swift b/Sources/Field.Coded.swift index d94e813..b02db6b 100644 --- a/Sources/Field.Coded.swift +++ b/Sources/Field.Coded.swift @@ -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." diff --git a/Sources/Field.Relationship.swift b/Sources/Field.Relationship.swift index f590327..486b207 100644 --- a/Sources/Field.Relationship.swift +++ b/Sources/Field.Relationship.swift @@ -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)) } diff --git a/Sources/Field.Stored.swift b/Sources/Field.Stored.swift index 29f5742..10e9663 100644 --- a/Sources/Field.Stored.swift +++ b/Sources/Field.Stored.swift @@ -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." diff --git a/Sources/Field.Virtual.swift b/Sources/Field.Virtual.swift index 815e18d..1437144 100644 --- a/Sources/Field.Virtual.swift +++ b/Sources/Field.Virtual.swift @@ -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." diff --git a/Sources/FieldAttributeProtocol.swift b/Sources/FieldAttributeProtocol.swift index 367434f..a2df2bd 100644 --- a/Sources/FieldAttributeProtocol.swift +++ b/Sources/FieldAttributeProtocol.swift @@ -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 } diff --git a/Sources/FieldProtocol.swift b/Sources/FieldProtocol.swift index 77b1981..83d3aff 100644 --- a/Sources/FieldProtocol.swift +++ b/Sources/FieldProtocol.swift @@ -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?) } diff --git a/Sources/ObjectProxy.swift b/Sources/ObjectProxy.swift index 89e233e..cd2f61e 100644 --- a/Sources/ObjectProxy.swift +++ b/Sources/ObjectProxy.swift @@ -133,11 +133,7 @@ public struct ObjectProxy { 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 { 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 { 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 = { diff --git a/Sources/ObjectPublisher.swift b/Sources/ObjectPublisher.swift index 23893c1..45a3ad1 100644 --- a/Sources/ObjectPublisher.swift +++ b/Sources/ObjectPublisher.swift @@ -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) }