Question about unowned members in actors

After watching WWDC22 session Visualize and optimize Swift concurrency, I have a question regarding the following sample code:

actor ParallelCompressor {
    // ......
    unowned let status: CompressionState
    
    // ......
    nonisolated func compressFile(url: URL) async -> Data {
        // ......
        let compressedData = CompressionUtils.compressDataInFile(at: url) { uncompressedSize in
            Task { @MainActor in
                status.update(url: url, uncompressedSize: uncompressedSize)
            }
        } // ......
    }
}

Here ParallelCompressor stores CompressionState as unowned let, but later there is code using this member in an escaping context. For me, it is fairly possible status could have been deinited when it is referenced in Task {}, so there might be a runtime crash, am I right?

After some digging, it seems declaring status as weak var is not an option, as posted in this thread: Actor Isolation and weak vars.

So my question is, is there a better and safer way to express such scenario, where an actor must reference a non-retained object?

1 Like

Sorry to reply to my own question, but I might just have found a workaround.
It seems there's a compiler hole using property wrappers.

By using a property wrapper, we can make the back value isolated, but the property accessors nonisolated.

@propertyWrapper
struct NonAssignableWeak<T: AnyObject> {
  private weak var value: T?
  init(wrappedValue: T?) {
    self.value = wrappedValue
  }

  init(_ value: T) {
    self.init(wrappedValue: value)
  }

  var wrappedValue: T? {
    value
  }
}

// now we can use
class X {}

actor A {
  init(_ incomingX: X) {
    self._x = NonAssignableWeak(incomingX)
  }

  @NonAssignableWeak
  nonisolated var x: X?

  // now we can use x wherever we want,
  // and we are always safe!
}