¿Cuándo usar @objc en Swift?

87

En Swift, veo algunos métodos como:

@objc private func doubleTapGestureRecognized(recognizer: UITapGestureRecognizer)

Me preguntaba, ¿cuándo usar @objc? Leí algunos documentos, pero dicen que cuando desee que se pueda llamar en Objective-C, debe agregar la bandera @objc

Sin embargo, esta es una función privada en Swift, ¿qué hace @obj?

Wingzero
fuente
1
buena pregunta !!!
Erhan Demirci

Respuestas:

67

privado significa que solo es visible en Swift. así que use @objc para ser visible en Objective-C. Si tiene una función para seleccionar una función privada en rápido, es necesaria.

El atributo @objc hace que su API Swift esté disponible en Objective-C y el tiempo de ejecución de Objective-C.

Ver: 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

Reming Hsu
fuente
1
por lo que para @objc private func doubleTapGestureRecognized, cuál es el punto de tener tanto @objc y privada? ¿Estás diciendo que las clases de Objective-C pueden sobrescribir doubleTapGestureRecognized?
Wingzero
1
Supongo que porque en obj-c puedes sobrescribir cualquier método de todos modos.
strangetimes
2
No estoy seguro de cómo esta respuesta es correcta y responde a su Q. La respuesta proporcionada aquí ya está en su Q misma "pero están diciendo que cuando quieras que se pueda llamar en Objective-C, debes agregar la bandera \ @objc", su principal P es "Esta es una función privada en Swift, ¿qué hace \ @obj?"
KBJ
3
Los enlaces se rompen. Especialmente en la documentación de Apple. Considere extraer los elementos clave de la documentación aquí.
levigroker
55

Otra respuesta tardía, pero ninguna de las respuestas existentes en esta pregunta realmente responde a la pregunta del OP, que es: ¿por qué diablos necesitaría usarla @objcen un privatemiembro de la clase, si @objcestá allí para interactuar con Objective-C y el miembro en cuestión? es privado, lo que significa que incluso si tiene código Objective-C en su proyecto, ¿no debería poder ver al miembro de todos modos?

La razón es que, debido a que muchos de los marcos están escritos en Objective-C, a veces se necesitan características de Objective-C para interactuar con ciertas API.

Por ejemplo, supongamos que quiero registrarme para recibir una notificación a través de DistributedNotificationCenter:

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

Para que esto funcione, necesitamos poder obtener el selector para el somethingHappenedmétodo. Sin embargo, los selectores son un concepto de Objective-C, por lo que si el método no es visible para Objective-C, no tiene selector. Por lo tanto, incluso si el método es privado y no debe ser llamado por código externo arbitrario, necesitará un @objcorden para que el DistributedNotificationcódigo, que está escrito en Objective-C, pueda llamarlo a través de su selector.

Otro caso común en el que @objcse necesita es admitir la codificación de valores clave (KVC), especialmente en macOS, donde se utilizan KVC y KVO para implementar Cocoa Bindings. KVC, como muchos otros sistemas en Cocoa, está implementado en Objective-C, lo que tiene el efecto de requerir que las propiedades compatibles con KVC estén expuestas al tiempo de ejecución de Objective-C. A veces, tiene sentido que las propiedades compatibles con KVC sean privadas. Un ejemplo es cuando tiene una propiedad que afecta a otras propiedades:

@objc private dynamic var originalProperty: String

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

En este caso, nuestra propiedad almacenada real es privada, pero la propiedad dependiente, que exponemos en el código externo, tiene que enviar sus notificaciones cuando la propiedad privada se actualiza. Al marcar la propiedad privada como @objc, podemos hacerlo fácilmente configurando una dependencia de KVC; de lo contrario, tendríamos que escribir código para enviar manualmente las notificaciones en los controladores willSety la propiedad privada didSet. Además, la propiedad estática que informa al sistema KVC del que dependentPropertydepende originalPropertydebe estar expuesta a Objective-C para que el sistema KVC lo encuentre y lo llame, pero no es relevante para los clientes de nuestro código.

Además, un controlador de vista en una aplicación macOS que actualiza los controles en su vista usando Cocoa Bindings como un detalle de implementación puede hacer que ciertas propiedades privadas sean compatibles con KVC para vincular esos controles a ellos.

Entonces, como puede ver, hay ocasiones en las que un método o propiedad puede necesitar exponerse a Objective-C para interactuar con los marcos, sin necesariamente tener que ser visible para los clientes de su código.

Charles Srstka
fuente
25

@objc / dinámico

Es por compatibilidad: una vez que importe su archivo / código Swift en un proyecto basado en Objective-C.

Y utilícelo si desea que el código o la clase de Objective-C acceda a su propiedad / método.

La mayoría de las veces sucede cuando está subclasificando una clase Swift de la clase base Objective-C.

Una clase o protocolo Swift debe estar marcado con el atributo @objc para que sea accesible y utilizable en Objective-C. Este atributo le dice al compilador que se puede acceder a esta parte del código Swift desde Objective-C. Si su clase Swift es descendiente de una clase Objective-C, el compilador agrega automáticamente el atributo @objc por usted.

Aquí la documentación de Apple que dice sobre @objc.

Usando Swift desde Objective-C

Compatibilidad de interoperabilidad de idiomas

Enlaces actualizados:
parece que Apple ha actualizado los enlaces.

0yeoj
fuente
11

@objc es un atributo de clase, por lo que usa

@objc public class MyClass

Expone los métodos de la clase a las clases de Objective C, por lo que solo lo usará si su clase contiene funciones públicas

Fraser
fuente
7

Una respuesta tardía, pero este @objccomportamiento está cambiando ligeramente a partir de Swift 4 (que salió en Xcode 9, que generalmente se lanzó hace 10 días).

En Swift 4, @objcse eliminan algunos casos de inferencia de . Esto solo significa que en algunos casos adicionales en los que antes de que el @objccompilador de Swift infiera el encabezado, no se infiere en Swift 4.

Leer más en la propuesta de evolución Swift sobre este cambio

Como se ha mencionado, en general se @objctrata de exponer ciertos métodos al tiempo de ejecución de Objective-C, que es parte de la interoperabilidad de Swift con el lenguaje.

BHendricks
fuente
1
entonces esta debería ser la razón por la cual después de actualizar a Swift 4 algunas variables no son accesibles desde ObjC, ¿verdad? Con Swift 3.X no hubo necesidad de agregar@objc
rgkobashi
oh ok, siempre es bueno volver a verificar con alguien más, ¡gracias!
rgkobashi
1

@objcexpone una declaración a Objective-C runtime. Echemos un vistazo a la #selectorfunción de Swift para usar un tiempo de ejecución de Objective-C. En este caso, puede definir su Swift @objc private func[Más]

Para usar las funciones de Swift de Objective-C:

  1. La clase de Swift debería extenderse de NSObject
  2. De Mark Swift:

    a. solo @objcMembers clase : para exponer todos los public constructores , campos y métodos . También es aplicable para subclases.

    segundo. @objc clase / enumeración / protocolo (excepto estructura ) [Tipo con nombre]

    • @objc class (opcional): para exponer un archivo public init() . O @objc(<custom_name>)para configurar un nombre personalizado para la clase.
    • @objc constructores , campos y métodos - para exponerlos selectivamente

El método de Swift estará disponible con el siguiente nombre:

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

Por ejemplo:

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

[ @objcy dynamic]

yoAlex5
fuente