[swift-evolution] Pitch: Remove `NonObjectiveCBase` and replace `isUniquelyReferenced` by `isUniquelyReferencedUnsafe`
Arnold Schwaighofer
aschwaighofer at apple.com
Sat Jul 16 14:47:43 CDT 2016
https://bugs.swift.org/browse/SR-1962
# Remove `NonObjectiveCBase` and replace `isUniquelyReferenced` by `isUniquelyReferencedUnsafe`
## Introduction
Remove `NonObjectiveCBase` and replace`isUniquelyReferenced<T: NonObjectiveCBase>(_ object: T)` by `isUniquelyReferencedUnsafe<T: AnyObject>(_ object: T)`. This will remove surface API. Instead of a type check dynamically check the non-`@objc` constraint under `-Onone`.
## Motivation
Today we have `isUniquelyReferenced` which only works on subclasses of
NonObjectiveCBase, and we have `isUniquelyReferencedNonObjC` which also works on
`@objc` classes.
```swift
class SwiftKlazz : NonObjectiveCBase {}
class ObjcKlazz : NSObject {}
expectTrue(isUniquelyReferenced(SwiftKlazz()))
expectFalse(isUniquelyReferencedNonObjC(ObjcKlazz()))
// Would not compile:
expectFalse(isUniquelyReferenced(ObjcKlazz()))
```
In most cases we expect developers to be using the ManagedBufferPointer type. In
cases where they want to use a custom class they would use
`isUniquelyReferenced` today and can use `isUniquelyReferencedUnsafe` in the
future.
```swift
class SwiftKlazz : NonObjectiveCBase {}
class ObjcKlazz : NSObject {}
expectTrue(isUniquelyReferencedUnsafe(SwiftKlazz()))
// Would trap under -Onone:
expectFalse(isUniquelyReferencedUnsafe(ObjcKlazz()))
```
Replacing `isUniquelyReferenced<T : NonObjectiveCBase>` by
`isUniquelyReferencedUnsafe<T: AnyObject>` will allow us to remove the
`NonObjectiveCBase` class from the standard library thereby shrink API surface.
We argue that trading type safety for less API surface is a good trade-off to
make with this low-level API.
## Proposed solution
Replace `isUniquelyReferenced<T : NonObjectiveCBase>` by
`isUniquelyReferencedUnsafe<T: AnyObject>` and remove the `NonObjectiveCBase`
class from the standard library.
## Detailed design
Remove the `NonObjectiveCBase` class and change
`isUniquelyReferenced<T: NonObjectiveCBase>(_ object: T>` to:
```swift
/// Returns `true` iff `object` is a non-`@objc` class instance with a single
/// strong reference. `object` is assumed to be a non-`@objc` class instance.
/// In debug mode this function will check this assumption. Otherwise, it is
/// undefined what happens.
///
/// * Does *not* modify `object`; the use of `inout` is an
/// implementation artifact.
/// * Weak references do not affect the result of this function.
///
/// Useful for implementing the copy-on-write optimization for the
/// deep storage of value types:
///
/// mutating func modifyMe(_ arg: X) {
/// if isUniquelyReferencedUnsafe(&myStorage) {
/// myStorage.modifyInPlace(arg)
/// }
/// else {
/// myStorage = myStorage.createModified(arg)
/// }
/// }
///
/// This function is safe to use for `mutating` functions in
/// multithreaded code because a false positive would imply that there
/// is already a user-level data race on the value being mutated.
public func isUniquelyReferencedUnsafe<T : AnyObject>(
_ object: inout T
) -> Bool {
_debugPrecondition(
_usesNativeSwiftReferenceCounting(object.dynamicType),
"instance must be a non- at objc class instance")
return _isUnique(&object)
}
```
Note, that today at -O we would actually not cause undefined behavior but
rather just return false. We don't want to guarantee this in the future so the
comment specifies undefined behavior.
## Impact on existing code
Existing code that uses `isUniquelyReferenced` will need to remove the
`NonObjectiveCBase` base class and replace calls to `isUniquelyReferenced` by
`isUniquelyReferencedUnsafe`. The old API will be marked unavailable to help migration.
## Alternatives considered
Leave the status quo and pay for type safety with additional API surface.
More information about the swift-evolution
mailing list