Skip to content

Conversation

@DougGregor
Copy link
Member

Stop importing the Objective-C protocol NSObject into Swift
programs, because it causes more harm than good. Specifically, with
this change:

  • The NSObjectProtocol protocol itself is not visible to Swift
    programs.
  • NSObjectProtocol is dropped from the inheritance list of any
    imported Objective-C protocol, e.g., UITableViewDelegate will not
    mention NSObjectProtocol in its inherited protocols.
  • NSObjectProtocol is removed from the list of protocols to which an
    imported Objective-C class conforms (e.g., the imported class
    NSObject will no longer state its conformance to
    NSObjectProtocol).
  • Qualified types in Objective-C will drop the NSObject protocol on
    import, e.g., an Objective-C type id<NSCopying, NSObject> will be
    imported as NSCopying rather than NSCopying & NSObjectProtocol. The type id<NSObject> will simply be imported
    as AnyObject.
  • Introduce a deprecated type alias into the ObjectiveC module overlay
    to ease transition:
    public typealias NSObjectProtocol = AnyObject

Stop importing the Objective-C protocol `NSObject` into Swift
programs, because it causes more harm than good. Specifically, with
this change:

* The `NSObjectProtocol` protocol itself is not visible to Swift
  programs.
* `NSObjectProtocol` is dropped from the inheritance list of any
  imported Objective-C protocol, e.g., `UITableViewDelegate` will not
  mention `NSObjectProtocol` in its inherited protocols.
* `NSObjectProtocol` is removed from the list of protocols to which an
  imported Objective-C class conforms (e.g., the imported class
  `NSObject` will no longer state its conformance to
  `NSObjectProtocol`).
* Qualified types in Objective-C will drop the `NSObject` protocol on
  import, e.g., an Objective-C type `id<NSCopying, NSObject>` will be
  imported as `NSCopying` rather than `NSCopying &
  NSObjectProtocol`. The type `id<NSObject>` will simply be imported
  as `AnyObject`.
* Introduce a deprecated type alias into the ObjectiveC module overlay
  to ease transition:
   ```swift
  public typealias NSObjectProtocol = AnyObject
  ```
@DougGregor
Copy link
Member Author

@swift-ci please test

@DougGregor
Copy link
Member Author

@swift-ci please test source compatibility

@DougGregor DougGregor changed the title [Objective-C interoperability] Eliminate import of NSObjectProtocol. [Experiment] [Objective-C interoperability] Eliminate import of NSObjectProtocol. Feb 15, 2018
@DougGregor DougGregor added the swift evolution pending discussion Flag → feature: A feature that has a Swift evolution proposal currently in review label Feb 15, 2018
We were emitting an error on the "inherit from NSObjectProtocol"
example when the Swift version was > 3; allow the specific use of the
NSObjectProtocol deprecated typealias here.

Thanks to Charlie Monroe for noticing the issue here!
@DougGregor
Copy link
Member Author

@swift-ci please test

1 similar comment
@DougGregor
Copy link
Member Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 8ec1b43

Copy link
Contributor

@jrose-apple jrose-apple left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code mostly looks good. I'll go comment on the forums about the semantic changes.


// For NSObject, add the otherwise-hidden NSObject protocol. This is only
// used to import mirrored members.
// FIXME: Check for any class that conforms to the NSObject protocol?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe NSProxy is the only other one in the Apple SDKs. We are absolutely terrible with it but if you wanted to avoid touching it maybe you could make the condition about root classes instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The principled thing would be to check whether it's a root class that adopts the protocol in Objective-C.

importedType = ProtocolCompositionType::get(Impl.SwiftContext,
members,
/*HasExplicitAnyObject=*/false);
/*HasExplicitAnyObject=*/hasNSObjectProtocol);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem so important when members is known to contain at least one @objc protocol. Or does this handle the 0 case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only really relevant for the 0 case; I can gate it on that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, this is the part that forces us to import such things as AnyObject rather than Any; @jckarter was arguing that we should import as Any.

@objc var description : String { return "" }
@objc(conformsToProtocol:) func conforms(to _: Protocol) -> Bool { return false }
@objc(isKindOfClass:) func isKind(of aClass: AnyClass) -> Bool { return false }
class NSObjectable : NSObject {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be more interesting if this didn't subclass anything, but it probably doesn't really matter at this point.

// CHECK: NSObject:
// CHECK-NEXT: TU: NSObject
// CHECK-NEXT: NSObjectProtocol:
// CHECK: __NSObjectProtocol:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the extra "Protocol" coming from? Does that kick in before the SwiftPrivate part?

// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
// CHECK-NEXT: @end
@objc class ClassWithNSObjectProtocol : NSObjectProtocol {
@objc class ClassWithNSObjectProtocol {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer testing the same thing; please have it conform to something else. (Preferably something where the Objective-C name doesn't match the Swift name.)

// CHECK-LABEL: @protocol MyProtocol
// CHECK-NEXT: @end
@objc protocol MyProtocol : NSObjectProtocol {}
@objc protocol MyProtocol {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here, since we're testing printing.

@IBOutlet var outletCollection: [Properties]!
@IBOutlet var outletCollectionOptional: [ClassWithCustomName]? = []
@IBOutlet var outletCollectionAnyObject: [AnyObject]?
@IBOutlet var outletCollectionProto: [NSObjectProtocol]?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here.

@shahmishal
Copy link
Member

Please update the base branch to main by Oct 5th otherwise the pull request will be closed automatically.

  • How to change the base branch: (Link)
  • More detail about the branch update: (Link)

@shahmishal shahmishal closed this Oct 5, 2020
@AnthonyLatsis AnthonyLatsis removed the swift evolution pending discussion Flag → feature: A feature that has a Swift evolution proposal currently in review label Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants