gnuoyd
(David Young)
8
I've been think how can this be reliably implemented for ObjC weak
references. And I think associated objects is the answer. I can
emulate side table behaviour by storing an associated object and using
identity of that object as a key.
This approach sounds like a reliable solution for Swift as well, but
for Swift it is suboptimal. I would prefer to use associated objects
approach as a fallback, and still have a fast path for native Swift
reference counting.
What if you stick the weak references into a dictionary indexed by
ObjectIdentifer and identified by serial number? Identify each WeakRef
by the serial number corresponding to the referenced object, if any has
already been assigned; otherwise, assign a new serial number. See the
following code, which I haven't even tried to compile:
struct WeakRef {
private struct ExtantRecord {
weak var object: MyClass?
var serialNumber: UInt64
}
private static var nextSerialNumber: UInt64 = 0
private static var extantWeakRefs: Dictionary<ObjectIdentifer,
> = [:]
private var serialNumber: UInt64
func hash(into hasher: inout Hasher) {
hasher.combine(l.serialNumber)
}
static func ==(_ l: WeakRef, _ r: WeakRef) -> Bool {
return l.serialNumber == r.serialNumber
}
init(object o: MyClass) {
let oid = ObjectIdentifier(o)
// TBD synchronize access to extantWeakRefs, nextSerialNumber
guard let extant = extantWeakRefs[oid], extant.object === o
else {
serialNumber = nextSerialNumber
nextSerialNumber = nextSerialNumber + 1
extantWeakRefs[oid] = ExtantRecord(object: o,
serialNumber: serialNumber)
// Collect garbage after every 1024 records.
if serialNumber % 1024 == 0 {
collectGarbage()
}
return
}
serialNumber = extant.serialNumber
}
// Remove records of weak refs that have changed to nil.
private static func collectGarbage() {
// TBD synchronize access to extantWeakRefs
extantWeakRefs = extantWeakRefs.compactMapValues() { extant in
(extant.object == nil) ? nil : extant
}
}
}
Dave