WIP: new PartialObject to act as faster KVC wrappers when implementing custom getters and setters for CoreStoreObject

This commit is contained in:
John Rommel Estropia
2017-06-15 08:27:08 +09:00
parent 5689158b43
commit 746d697691
5 changed files with 233 additions and 105 deletions

View File

@@ -242,6 +242,10 @@
B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; };
B53B27611EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; };
B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; };
B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; };
B53CA9A31EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; };
B53CA9A41EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; };
B53CA9A51EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; };
B53FB9FE1CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
B53FBA001CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
B53FBA011CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
@@ -757,6 +761,7 @@
B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+CoreStore.swift"; sourceTree = "<group>"; };
B538BA701D15B3E30003A766 /* CoreStoreBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CoreStoreBridge.m; sourceTree = "<group>"; };
B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreManagedObject.swift; sourceTree = "<group>"; };
B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialObject.swift; sourceTree = "<group>"; };
B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSMigrationResult.swift; sourceTree = "<group>"; };
B53FBA031CAB300C00F0D40A /* CSMigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSMigrationType.swift; sourceTree = "<group>"; };
B53FBA0A1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSCoreStore+Migrating.swift"; sourceTree = "<group>"; };
@@ -1229,6 +1234,7 @@
isa = PBXGroup;
children = (
B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */,
B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */,
B52F74391E9B8724005F3DAC /* Dynamic Schema */,
B5D339DC1E9489C700C880DE /* DynamicObject.swift */,
B52F742E1E9B50D0005F3DAC /* SchemaHistory.swift */,
@@ -1826,6 +1832,7 @@
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */,
B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */,
B546F9731C9C553300D5AC55 /* SetupResult.swift in Sources */,
B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */,
B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */,
B5E84F111AFF847B0064E85B /* Select.swift in Sources */,
B51260931E9B28F100402229 /* EntityIdentifier.swift in Sources */,
@@ -2011,6 +2018,7 @@
82BA18A31C4BBD2200A0916E /* DataStack.swift in Sources */,
82BA18C81C4BBD5900A0916E /* MigrationChain.swift in Sources */,
B546F9741C9C553300D5AC55 /* SetupResult.swift in Sources */,
B53CA9A31EF1EF1600E0F440 /* PartialObject.swift in Sources */,
82BA18B11C4BBD3100A0916E /* SaveResult.swift in Sources */,
82BA18DD1C4BBE1400A0916E /* NSFetchedResultsController+Convenience.swift in Sources */,
B51260941E9B28F100402229 /* EntityIdentifier.swift in Sources */,
@@ -2196,6 +2204,7 @@
B52DD1961BE1F92500949AFE /* DataStack.swift in Sources */,
B5ECDBFD1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */,
B52DD1BD1BE1F94300949AFE /* NSManagedObject+Convenience.swift in Sources */,
B53CA9A51EF1EF1600E0F440 /* PartialObject.swift in Sources */,
B52DD1AD1BE1F93900949AFE /* Where.swift in Sources */,
B53FBA1C1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */,
B51260961E9B28F100402229 /* EntityIdentifier.swift in Sources */,
@@ -2381,6 +2390,7 @@
B56321A81BD65219006C9394 /* NSManagedObject+Convenience.swift in Sources */,
B546F9751C9C553300D5AC55 /* SetupResult.swift in Sources */,
B56321981BD65216006C9394 /* Where.swift in Sources */,
B53CA9A41EF1EF1600E0F440 /* PartialObject.swift in Sources */,
B5202CFD1C046E8400DED140 /* NSFetchedResultsController+Convenience.swift in Sources */,
B5FE4DA91C84FB4400FA6A91 /* InMemoryStore.swift in Sources */,
B51260951E9B28F100402229 /* EntityIdentifier.swift in Sources */,

View File

@@ -135,3 +135,23 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
}
}
}
// MARK: - DynamicObject where Self: CoreStoreObject
public extension DynamicObject where Self: CoreStoreObject {
public func partialObject() -> PartialObject<Self> {
CoreStore.assert(
!self.isMeta,
"Attempted to create a \(cs_typeName(PartialObject<Self>.self)) from a meta object. Meta objects are only used for querying keyPaths and infering types."
)
return PartialObject<Self>(self.rawObject!)
}
internal static var meta: Self {
return self.init(asMeta: ())
}
}

View File

@@ -155,14 +155,3 @@ extension CoreStoreObject {
return self.rawObject!
}
}
// MARK: - Internal
internal extension DynamicObject where Self: CoreStoreObject {
internal static var meta: Self {
return self.init(asMeta: ())
}
}

135
Sources/PartialObject.swift Normal file
View File

@@ -0,0 +1,135 @@
//
// PartialObject.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 CoreData
import Foundation
// MARK: - PartialObject
public struct PartialObject<O: CoreStoreObject> {
public func completeObject() -> O {
return O.cs_fromRaw(object: self.rawObject)
}
public func value<V: ImportableAttributeType>(for property: (O) -> ValueContainer<O>.Required<V>) -> V {
return V.cs_fromImportableNativeType(
self.rawObject.value(forKey: property(O.meta).keyPath)! as! V.ImportableNativeType
)!
}
public func value<V: ImportableAttributeType>(for property: (O) -> ValueContainer<O>.Optional<V>) -> V? {
return (self.rawObject.value(forKey: property(O.meta).keyPath) as! V.ImportableNativeType?)
.flatMap(V.cs_fromImportableNativeType)
}
public func value<V: NSCoding & NSCopying>(for property: (O) -> TransformableContainer<O>.Required<V>) -> V {
return self.rawObject.value(forKey: property(O.meta).keyPath)! as! V
}
public func value<V: NSCoding & NSCopying>(for property: (O) -> TransformableContainer<O>.Optional<V>) -> V? {
return self.rawObject.value(forKey: property(O.meta).keyPath) as! V?
}
public func setValue<V: ImportableAttributeType>(_ value: V, for property: (O) -> ValueContainer<O>.Required<V>) {
self.rawObject.setValue(
value.cs_toImportableNativeType(),
forKey: property(O.meta).keyPath
)
}
public func setValue<V: ImportableAttributeType>(_ value: V?, for property: (O) -> ValueContainer<O>.Optional<V>) {
self.rawObject.setValue(
value?.cs_toImportableNativeType(),
forKey: property(O.meta).keyPath
)
}
public func setValue<V: NSCoding & NSCopying>(_ value: V, for property: (O) -> TransformableContainer<O>.Required<V>) {
self.rawObject.setValue(
value,
forKey: property(O.meta).keyPath
)
}
public func setValue<V: NSCoding & NSCopying>(_ value: V?, for property: (O) -> TransformableContainer<O>.Optional<V>) {
self.rawObject.setValue(
value,
forKey: property(O.meta).keyPath
)
}
public func setPrimitiveValue<V: ImportableAttributeType>(_ value: V, for property: (O) -> ValueContainer<O>.Required<V>) {
self.rawObject.setPrimitiveValue(
value.cs_toImportableNativeType(),
forKey: property(O.meta).keyPath
)
}
public func setPrimitiveValue<V: ImportableAttributeType>(_ value: V?, for property: (O) -> ValueContainer<O>.Optional<V>) {
self.rawObject.setPrimitiveValue(
value?.cs_toImportableNativeType(),
forKey: property(O.meta).keyPath
)
}
public func setPrimitiveValue<V: NSCoding & NSCopying>(_ value: V, for property: (O) -> TransformableContainer<O>.Required<V>) {
self.rawObject.setPrimitiveValue(
value,
forKey: property(O.meta).keyPath
)
}
public func setPrimitiveValue<V: NSCoding & NSCopying>(_ value: V?, for property: (O) -> TransformableContainer<O>.Optional<V>) {
self.rawObject.setPrimitiveValue(
value,
forKey: property(O.meta).keyPath
)
}
// MARK: Internal
internal let rawObject: NSManagedObject
internal init(_ rawObject: NSManagedObject) {
self.rawObject = rawObject
}
}

View File

@@ -123,8 +123,8 @@ public enum ValueContainer<O: CoreStoreObject> {
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
@@ -155,16 +155,13 @@ public enum ValueContainer<O: CoreStoreObject> {
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
let customGetter = (self.customGetter ?? { $1() })
return customGetter(
object,
{ () -> V in
return V.cs_fromImportableNativeType(
object.rawObject!.value(forKey: self.keyPath)! as! V.ImportableNativeType
)!
}
)
if let customGetter = self.customGetter {
return customGetter(PartialObject<O>(object.rawObject!))
}
return V.cs_fromImportableNativeType(
object.rawObject!.value(forKey: self.keyPath)! as! V.ImportableNativeType
)!
}
}
set {
@@ -183,17 +180,13 @@ public enum ValueContainer<O: CoreStoreObject> {
object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
let customSetter = (self.customSetter ?? { $1($2) })
customSetter(
object,
{ (newValue: V) -> Void in
object.rawObject!.setValue(
newValue.cs_toImportableNativeType(),
forKey: self.keyPath
)
},
newValue
if let customSetter = self.customSetter {
return customSetter(PartialObject<O>(object.rawObject!), newValue)
}
return object.rawObject!.setValue(
newValue.cs_toImportableNativeType(),
forKey: self.keyPath
)
}
}
@@ -274,15 +267,12 @@ public enum ValueContainer<O: CoreStoreObject> {
return { (_ id: Any) -> Any? in
let rawObject = id as! CoreStoreManagedObject
let value = customGetter(
O.cs_fromRaw(object: rawObject),
{
rawObject.getValue(
forKvcKey: keyPath,
didGetValue: { V.cs_fromImportableNativeType($0 as! V.ImportableNativeType!)! }
)
}
)
rawObject.willAccessValue(forKey: keyPath)
defer {
rawObject.didAccessValue(forKey: keyPath)
}
let value = customGetter(PartialObject<O>(rawObject))
return value.cs_toImportableNativeType()
}
}
@@ -297,16 +287,13 @@ public enum ValueContainer<O: CoreStoreObject> {
return { (_ id: Any, _ newValue: Any?) -> Void in
let rawObject = id as! CoreStoreManagedObject
rawObject.willChangeValue(forKey: keyPath)
defer {
rawObject.didChangeValue(forKey: keyPath)
}
customSetter(
O.cs_fromRaw(object: rawObject),
{ (userValue: V) -> Void in
rawObject.setValue(
userValue,
forKvcKey: keyPath,
willSetValue: { $0.cs_toImportableNativeType() }
)
},
PartialObject<O>(rawObject),
V.cs_fromImportableNativeType(newValue as! V.ImportableNativeType)!
)
}
@@ -315,8 +302,8 @@ public enum ValueContainer<O: CoreStoreObject> {
// MARK: Private
private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)?
private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)?
private let customGetter: ((_ partialObject: PartialObject<O>) -> V)?
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)?
}
@@ -370,8 +357,8 @@ public enum ValueContainer<O: CoreStoreObject> {
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil,
customGetter: ((_ partialObject: PartialObject<O>) -> V?)? = nil,
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
self.keyPath = keyPath
@@ -402,15 +389,12 @@ public enum ValueContainer<O: CoreStoreObject> {
object.rawObject!.isRunningInAllowedQueue() == true,
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
)
let customGetter = (self.customGetter ?? { $1() })
return customGetter(
object,
{ () -> V? in
return (object.rawObject!.value(forKey: self.keyPath) as! V.ImportableNativeType?)
.flatMap(V.cs_fromImportableNativeType)
}
)
if let customGetter = self.customGetter {
return customGetter(PartialObject<O>(object.rawObject!))
}
return (object.rawObject!.value(forKey: self.keyPath) as! V.ImportableNativeType?)
.flatMap(V.cs_fromImportableNativeType)
}
}
set {
@@ -429,17 +413,13 @@ public enum ValueContainer<O: CoreStoreObject> {
object.rawObject!.isEditableInContext() == true,
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
)
let customSetter = (self.customSetter ?? { $1($2) })
customSetter(
object,
{ (newValue: V?) -> Void in
object.rawObject!.setValue(
newValue?.cs_toImportableNativeType(),
forKey: self.keyPath
)
},
newValue
if let customSetter = self.customSetter {
return customSetter(PartialObject<O>(object.rawObject!), newValue)
}
object.rawObject!.setValue(
newValue?.cs_toImportableNativeType(),
forKey: self.keyPath
)
}
}
@@ -518,15 +498,12 @@ public enum ValueContainer<O: CoreStoreObject> {
return { (_ id: Any) -> Any? in
let rawObject = id as! CoreStoreManagedObject
let value = customGetter(
O.cs_fromRaw(object: rawObject),
{
rawObject.getValue(
forKvcKey: keyPath,
didGetValue: { ($0 as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) }
)
}
)
rawObject.willAccessValue(forKey: keyPath)
defer {
rawObject.didAccessValue(forKey: keyPath)
}
let value = customGetter(PartialObject<O>(rawObject))
return value?.cs_toImportableNativeType()
}
}
@@ -541,16 +518,13 @@ public enum ValueContainer<O: CoreStoreObject> {
return { (_ id: Any, _ newValue: Any?) -> Void in
let rawObject = id as! CoreStoreManagedObject
rawObject.willChangeValue(forKey: keyPath)
defer {
rawObject.didChangeValue(forKey: keyPath)
}
customSetter(
O.cs_fromRaw(object: rawObject),
{ (userValue: V?) -> Void in
rawObject.setValue(
userValue,
forKvcKey: keyPath,
willSetValue: { $0?.cs_toImportableNativeType() }
)
},
PartialObject<O>(rawObject),
(newValue as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType)
)
}
@@ -559,8 +533,8 @@ public enum ValueContainer<O: CoreStoreObject> {
// MARK: Private
private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)?
private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)?
private let customGetter: ((_ partialObject: PartialObject<O>) -> V?)?
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)?
}
}
@@ -593,7 +567,7 @@ public extension ValueContainer.Required where V: EmptyableAttributeType {
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
customGetter: ((_ `self`: PartialObject<O>, _ getValue: () -> V) -> V)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
@@ -670,7 +644,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
customGetter: ((_ `self`: PartialObject<O>, _ getValue: () -> V) -> V)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
@@ -704,7 +678,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
)
let customGetter = (self.customGetter ?? { $1() })
return customGetter(
object,
PartialObject<O>(object.rawObject!),
{ () -> V in
return object.rawObject!.value(forKey: self.keyPath)! as! V
@@ -818,7 +792,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
let rawObject = id as! CoreStoreManagedObject
return customGetter(
O.cs_fromRaw(object: rawObject),
PartialObject<O>(rawObject),
{ rawObject.getValue(forKvcKey: keyPath) as! V }
)
}
@@ -848,7 +822,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
// MARK: Private
private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)?
private let customGetter: ((_ `self`: PartialObject<O>, _ getValue: () -> V) -> V)?
private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)?
}
@@ -897,7 +871,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
isTransient: Bool = false,
versionHashModifier: String? = nil,
renamingIdentifier: String? = nil,
customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil,
customGetter: ((_ `self`: PartialObject<O>, _ getValue: () -> V?) -> V?)? = nil,
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil,
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
@@ -931,7 +905,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
)
let customGetter = (self.customGetter ?? { $1() })
return customGetter(
object,
PartialObject<O>(object.rawObject!),
{ () -> V? in
object.rawObject!.value(forKey: self.keyPath) as! V?
@@ -1045,7 +1019,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
let rawObject = id as! CoreStoreManagedObject
return customGetter(
O.cs_fromRaw(object: rawObject),
PartialObject<O>(rawObject),
{ rawObject.getValue(forKvcKey: keyPath) as! V? }
)
}
@@ -1083,7 +1057,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
// MARK: Private
private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)?
private let customGetter: ((_ `self`: PartialObject<O>, _ getValue: () -> V?) -> V?)?
private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)?
}
}