There's something to be said for presentation of an interface being part of defining a library, even if it's unenforceable. But I agree with Michel otherwise: if we take this away, we shouldn't print it at all, or else we've made going from stored to computed property observable in the generated interface.
I'm shifting my position a little bit based on the feedback in this thread.
In the case of (say) a "
delegate" property, it is of course vital to know whether the object takes ownership or not. It's typical (in Apple frameworks) that it's zeroing weak, but some are not, so it's important to "mark" the exceptions.
In the case of a stored property in Swift (and a synthesized @property in ObjC), it's a very natural alignment for
weak to be both a storage annotation and an API contract.
In the case of a computed property in Swift (and an ObjC @property with a custom implementation), there is a great possibility of error if the
weak annotation is "only" an API contract, and not something that the compiler somehow enforces or validates.
This is not a theoretical problem in ObjC, but an actual source of memory management bugs.
weak on computed properties works as API documentation, but easily becomes a source of bugs. Outlawing it is safer, but forces the documentation to be separate from the API.
Both seem viable, but opinion seems to be split on which is better.
Inside the setter of a
weak computed property, could we issue a warning on assigning
newValue to a non-weak stored property of
self? That'd make the annotation useful, even though it'd be easy to evade.
which says it makes sense to have weak on computed properties. is it still true?
weak is not part of the type in the first place? Being able to write
var delegates: [weak Delegate] would be handy. And this would make contact clear. And if weak reference would be less aggressive about dropping zombie side tables, they would be trivially hashable.
It is now possible to have a struct with a weak reference as a single field. Such struct can be used as a return type and type of arguments, it can be used as a value for generic type parameters. But also, being a single field struct it has a memory layout identical to a weak reference. So, de-facto we can use weak references as types.
This is probably a mistake in the Swift type system, and I'm not sure if this could be reasonably fixed at this point. You can work around this by defining a
Weak<T> type that has a weak property internally.
That's pretty much what I ended up doing. But my current solution relies on associated objects, rather than side table, which limits it to Apple platforms. And using custom token instead of side table requires an extra allocation.
And also, currently I cannot express constraint for
T that would enable using both classes and existential types. According to Existential subtyping as generic constraint by nickolas-pohilets · Pull Request #28741 · apple/swift · GitHub, it would be
Weak<T: any AnyObject>. BTW, could someone approve the MR, or at least jump in to continue discussion. @codafi?
Is this really a mistake? If it was part of the type,
weak would implicitly propagate everywhere you copy the variable to, just like implicitly unwrapped optionals did before SE-0054. IUOs were changed because a proliferation of IUOs was considered undesirable. Wouldn't it be even less desirable for
I don’t think that analogy between
weak and IUO is correct. IUO is a transitional technology, and so probably is
unowned(unsafe), but I’m pretty sure
unowned are here to stay. Some mechanism to support weak references is essential for any reference-counting-based system and Swift is not an exception. Even if two will be merged into one for simplicity in the future (hypothetically), at least one of them will remain for sure.
@michelf, do you see any problems with using weak references as types in these or any other cases?
- using weak reference as a type of element/key/value of the container
- using weak reference as a type of function argument/result
- returning weak reference type when inspecting object using Mirror
Should we... um, spawn a new thread?
Moving that discussion to a new thread: Should weak be a type?