Objective-C interoperability: Eliminate NSObjectProtocol

Making the Swift compatibility root class an NSObject subclass doesn't mean that we'd expose Swift root classes as NSObject subclasses in Swift; it'd still be an implementation detail just like SwiftObject is today.

Yes, but:

protocol Foo {
    func bar()
}

extension NSObject: Foo { ... }

class Bar { /* No NSObject inheritance. */ }

let x = Bar()

Now, x is Foo is evaluated as false. After adding NSObject inheritance to the SwiftObject, it will be true, eventually removing it will cause it to be false again.

The same goes with anything else - -respondsToSelector:, etc.

So while it will be an implementation detail, it either means keeping the NSObject around forever, or removing the inheritance eventually, breaking some code.

1 Like

It's not entirely an implementation detail, because as? casts to NSObject would start succeeding, which would make portability to platforms without the Objective-C runtime harder.

  • Doug

Yes, this is a great point. We can remove the NSObject inheritance requirement from the source code, but writing a delegate without NSObject inheritance might cause many problems interacting with the underlying frameworks. Those are "just bugs" that should be fixed either by adding more to SwiftObject or by fixing the frameworks to not require NSObject, but if there are enough of them, it makes the removal of NSObject inheritance unworkable in practice.

Doug

We do control what dynamic casting does for Swift classes. We could make it so that as? NSObject still fails for "pure" Swift classes even if the runtime root happens to inherit NSObject for better interoperability.

@charlieMonroe's example still bleeds through into the user-visible model, although I guess we can plug that with more runtime changes. To me, it feels like adding NSObject as a hidden superclass and plugging the holes in the user-visible model (especially if we happen to ship with any of those holes) is more difficult that continuing with the status quo, because the status quo is more restrictive.

Doug

You don't always know in advance when you will need KVO. For example, you might have a model object from one framework (maybe a 3rd-party library) and then one day you want to display them inside a MKMapView, but then you realise that the MKAnnotation protocol requires NSObject (it uses KVO, maybe there are other reasons too).

I had a situation like this recently. The only thing I could do was to wrap the objects in a generic class, which leads to many more allocations. The model objects were Swift classes (not structs), so IIUC from this pitch, those extra allocations are avoidable.

I shipped that work off to a background queue, but it would still be better to avoid it entirely.

1 Like

There are two levels of consideration to any app, regardless of whether it's authored in Objective-C or Swift: compile-time and run-time. The ObjcC runtime framework is the way for developers to address the considerations made on the latter. If anyone is proposing that this should not be so, my natural next question would be: then what in its place, instead?

There are tons of valid concerns here that I have no doubt have relevance, but the answer is not to extract what is otherwise a complete, solid (time-tested) and never any less than inextricable component of developing apps (the relative value of the framework itself notwithstanding).

There is no question as to whether it should exist. It must by any account of reason.