什么时候在 Swift 中使用@objecc?

在 Swift 中,我看到了一些方法,比如:

@objc private func doubleTapGestureRecognized(recognizer: UITapGestureRecognizer)

我想知道,什么时候使用@obc?我读了一些文档,但是它们说当你想要在 Objective-C 中调用它时,你应该添加@objecc 标志

但是,这是 Swift 中的私有函数,@obj 是做什么的?

61891 次浏览

@objc is a class attribute, so you use

@objc public class MyClass

It exposes the class' methods to Objective C classes, so you'll only use it if your class contains public functions

private mean it visible only in Swift. so use @objc to visible in Objective-C. If you have a func to selector a private func in swift, it is required.

The @objc attribute makes your Swift API available in Objective-C and the Objective-C runtime.

See: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html

@objc / dynamic

It's for compatibility: Once you import your Swift file/code into Objective-C based project.

And use that if you want your property/method to be accessed by Objective-C code or class.

Most of the time it happens when you are sub classing a Swift class of Objective-C base class.

A Swift class or protocol must be marked with the @objc attribute to be accessible and usable in Objective-C. This attribute tells the compiler that this piece of Swift code can be accessed from Objective-C. If your Swift class is a descendant of an Objective-C class, the compiler automatically adds the @objc attribute for you.

Here apple documentation that says about @objc.

Using Swift from Objective-C

Language Interoperability Compatibility

Links Updated:
Looks like the links has been updated by apple.

A late answer, but this @objc behavior is changing slightly as of Swift 4 (which came out in Xcode 9, which was generally released 10 days ago).

In Swift 4, some inference cases of @objc are removed. This just means in some additional cases where before the @objc header was inferred by the Swift compiler, it's in Swift 4 not inferred.

Read more at the Swift evolution proposal about this change

As has been mentioned, in general @objc is to expose certain methods to the Objective-C runtime, which is part of Swift's interoperability the language.

Another late answer, but none of the existing answers on this question really answer the OP's question, which is: why the heck would you need to use @objc on a private class member, if @objc is there for interaction with Objective-C, and the member in question is private, meaning that even if you have Objective-C code in your project, it shouldn't be able to see the member anyway?

The reason is that, because many of the frameworks are written in Objective-C, sometimes Objective-C features are needed to interact with certain APIs.

For example, suppose I want to register for a notification via DistributedNotificationCenter:

DistributedNotificationCenter.default.addObserver(self,
selector: #selector(somethingHappened(_:)),
name: someNotification,
object: nil)

For this to work, we need to be able to get the selector for the somethingHappened method. However, selectors are an Objective-C concept, so if the method is not visible to Objective-C, it does not have a selector. Therefore, even if the method is private and should not be called by arbitrary outside code, it will need an @objc in order for the DistributedNotification code, which is written in Objective-C, to be able to call it via its selector.

Another common case where @objc is needed is to support Key-Value Coding (KVC), especially on macOS, where KVC and KVO are used to implement Cocoa Bindings. KVC is, like many other systems in Cocoa, implemented in Objective-C, which has the effect of requiring KVC-compliant properties to be exposed to the Objective-C runtime. Sometimes, it makes sense for KVC-compliant properties to be private. One example is when you have a property that affects other properties:

@objc private dynamic var originalProperty: String


@objc private static let keyPathsForValuesAffectingDependentProperty: Set<String> = [
#keyPath(originalProperty)
]
@objc public var dependentProperty: String { return changeItSomehow(self.originalProperty) }

In this case, our actual stored property is private, but the dependent property, which we do expose to outside code, needs to send its notifications when the private property is updated. By marking the private property as @objc, we can easily do that by setting up a KVC dependency—otherwise, we'd have to write code to manually send the notifications in the private property's willSet and didSet handlers. In addition, the static property that informs the KVC system that dependentProperty is dependent on originalProperty needs to be exposed to Objective-C so that the KVC system and find it and call it, but it's not relevant to clients of our code.

Also, a view controller in a macOS app that updates controls in its view using Cocoa Bindings as an implementation detail may make certain private properties KVC-compliant in order to bind those controls to them.

So as you see, there are times when a method or property may need to be exposed to Objective-C in order to interact with the frameworks, without necessarily needing to be visible to clients of your code.

@objc vs @objcMembers

@objc exposes a declaration to Objective-C runtime[About]. Let's take a look at #selector[About] feature of Swift to use an Objective-C runtime. In this case you are able to define your Swift @objc private func[More]

To use Swift's functions from Objective-C:

  1. Swift's class should be extended from NSObject
  2. Mark Swift's:

a. @objcMembers class only - to expose all public constructors, fields and methods. Also it is applicable for subclasses

b. @objc class/enum/protocol (except struct)[Named Type]

  • @objc class(optional) - to expose a default public init(). Or @objc(<custom_name>) to setup a custom name for class.
  • @objc constructors, fields and methods - to expose them selectively

Swift's method will be available by the next naming:

<swiftName>With<firstArgument>:<secondArgument>:

For example:

public func printHelloWorld(arg1: String, arg2:String)
//is reached through:
[someObject printHelloWorldWithArg1: arg2:];

[@objc and dynamic]