[swift-evolution] [Review] SE-0160: Limiting @objc inference

Douglas Gregor dgregor at apple.com
Fri Mar 24 12:09:33 CDT 2017

> On Mar 22, 2017, at 10:55 PM, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:
>> On Mar 21, 2017, at 11:03 PM, Chris Lattner via swift-evolution <swift-evolution at swift.org> wrote:
>> * What is your evaluation of the proposal?
> I'm going to have to come down on the "no" side on this one.
> I'm actually not worried about methods so much as properties. KVC is completely untyped on the Objective-C side, and there are several different mechanisms there which use KVC with poorly validated external strings, like bindings, sort descriptors, and predicates. Tons of migration errors are going to escape into production if we do this,

We can avoid these by migrating conservatively (have the migrator add @objc everywhere it’s inferred in Swift 3).

> and undetectable mistakes are going to continue on an ongoing basis. 

… but what you say above is definitely true: the error of omission of @objc will cause breakage for these cases. 

> Have we explored alternate implementations? For instance, when the compiler can statically determine all of the call sites for an `@objc` member, could we emit *only* an implementation with the Objective-C calling convention and call that directly from Swift?
> Could members with binary-compatible signatures share a thunk? The Objective-C calling convention includes a selector parameter, so you can still determine the desired message.
> Could we give implicit methods and properties a different, slower implementation that leverages Objective-C's dynamic features? Imagine, for instance, mapping in `imp_implementationWithBlock()`-style pages of general-purpose thunks at runtime to reduce code size, then installing them lazily with `+resolveClassMethod:`. (Maybe this could actually be made fast enough, amortized over all calls, that we could get rid of the thunks entirely—I don't know.)

(Slava talked through some of these)

> Have you evaluated applying different rules to methods and properties?

For me, I’d rather reject the proposal as a whole than further complicate the rules for @objc inference.

> Have you considered a deprecation cycle (for instance, having Swift 4 thunks log a warning that they're going away in Swift 5)?

I think Swift 3 -> Swift 4 is the deprecation cycle, no?

> Or is the real motivation that, code size issues aside, you think these members ought to be explicitly marked `@objc` for philosophical reasons?

That’s certainly *a* reason. The @objc inference rules are fairly complicated and even experienced developers can’t easily guess whether something will be exposed to Objective-C or not because (e.g.) minor changes in the parameter/result types of a method can affect it.

> If so, how many times do you want people to say so?

Once per API that’s exposed to Objective-C.

> You already have to explicitly inherit from an `@objc` base class;

You don’t *need* to inherit from an `@objc` base class to have an @objc member, but @objc inference ties together the notions of 

Plus, inheritance from an Objective-C class is often incidental: you do it because you need an NSObjectProtocol conformance, or something else expects NSObject. I haven’t heard of developers inheriting from NSObject solely to get @objc inference for their members.

> you already have to specify `dynamic` to avoid optimizations;

Conceptually, ‘dynamic’ is orthogonal to ‘@objc’. In today’s implementation, we can only implement ‘dynamic’ via the Objective-C runtime, hence this proposal’s requirement to write both.

> now you also have to mark individual members `@objc`? Would you like the request for bridging notarized and filed in triplicate?

> I can understand the impulse to require it explicitly on `public` members, but on the other hand, if you *do* accidentally publish a member as `@objc`, what's the harm? Is there any non-breaking change you can make to a `@nonobjc` method which wouldn't be legal on an `@objc` one?

There probably are; making an Int parameter of a final method Int? isn’t generally source-breaking, but means that you can no longer expose an @objc entrypoint. That said...

> Or can you just deprecate the `@objc` version and move on?

it’s easy to leave a deprecated @objc entrypoint in place if this happened to you, so I don’t think “accidentally made an @objc API that I wanted to be @nonobjc and now I have to support it going forward” is  strong argument in favor of this proposal.

	- Doug

More information about the swift-evolution mailing list