[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