Quand utiliser @objc dans Swift?

87

Dans Swift, je vois quelques méthodes comme:

@objc private func doubleTapGestureRecognized(recognizer: UITapGestureRecognizer)

Je me demandais quand utiliser @objc? J'ai lu certains documents, mais ils disent que lorsque vous voulez qu'il soit appelable en Objective-C, vous devez ajouter le drapeau @objc

Cependant, il s'agit d'une fonction privée dans Swift, que fait le @obj?

Wingzero
la source
1
bonne question !!!
Erhan Demirci

Réponses:

67

privé signifie qu'il n'est visible que dans Swift. utilisez donc @objc pour être visible dans Objective-C. Si vous avez une fonction pour sélectionner une fonction privée dans swift, elle est obligatoire.

L'attribut @objc rend votre API Swift disponible dans Objective-C et le runtime Objective-C.

Voir: 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
la source
1
Alors pour @objc private func doubleTapGestureRecognized, quel est l'intérêt d'avoir à la fois @objc et privé? Êtes-vous en train de dire que les classes Objective-C peuvent écraser doubleTapGestureRecognized?
Wingzero
1
Je suppose, car dans obj-c, vous pouvez quand même écraser n'importe quelle méthode.
strangetimes
2
Je ne sais pas comment cette réponse est correcte et répond à son Q. Les ans fournis ici sont déjà dans son Q lui-même "mais ils disent que lorsque vous voulez qu'il soit appelable en Objective-C, vous devez ajouter \ @objc flag", son principal Q est "Ceci est une fonction privée dans Swift, que fait le \ @obj?"
KBJ
3
Les liens se rompent. Surtout dans la documentation Apple. Veuillez envisager d'extraire les éléments clés de la documentation ici.
levigroker
55

Une autre réponse tardive, mais aucune des réponses existantes à cette question ne répond vraiment à la question du PO, à savoir: pourquoi diable auriez-vous besoin d'utiliser @objcsur un privatemembre de la classe, s'il @objcy a une interaction avec Objective-C, et le membre en question est privé, ce qui signifie que même si vous avez du code Objective-C dans votre projet, il ne devrait pas pouvoir voir le membre de toute façon?

La raison en est que, comme la plupart des frameworks sont écrits en Objective-C, des fonctionnalités Objective-C sont parfois nécessaires pour interagir avec certaines API.

Par exemple, supposons que je souhaite m'inscrire à une notification via DistributedNotificationCenter:

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

Pour que cela fonctionne, nous devons être en mesure d'obtenir le sélecteur de la somethingHappenedméthode. Cependant, les sélecteurs sont un concept Objective-C, donc si la méthode n'est pas visible par Objective-C, elle n'a pas de sélecteur. Par conséquent, même si la méthode est privée et ne doit pas être appelée par du code extérieur arbitraire, elle aura besoin d'un @objcin order pour que le DistributedNotificationcode, qui est écrit en Objective-C, puisse l'appeler via son sélecteur.

Un autre cas courant où il @objcest nécessaire est de prendre en charge le codage clé-valeur (KVC), en particulier sur macOS, où KVC et KVO sont utilisés pour implémenter les liaisons Cocoa. KVC est, comme beaucoup d'autres systèmes dans Cocoa, implémenté en Objective-C, ce qui a pour effet d'exiger que les propriétés compatibles KVC soient exposées au runtime Objective-C. Parfois, il est logique que les propriétés compatibles KVC soient privées. Un exemple est lorsque vous avez une propriété qui affecte d'autres propriétés:

@objc private dynamic var originalProperty: String

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

Dans ce cas, notre propriété réelle stockée est privée, mais la propriété à charge, que nous n'exposons à un code externe, doit envoyer ses notifications lors de la mise à jour de la propriété privée. En marquant la propriété privée comme , nous pouvons facilement le faire en configurant une dépendance KVC - sinon, nous aurions à écrire du code pour envoyer manuellement les notifications dans la propriété privée et les gestionnaires. En outre, la propriété statique qui informe le système KVC qui dépend de doit être exposée à Objective-C afin que le système KVC le trouve et l'appelle, mais ce n'est pas pertinent pour les clients de notre code.@objcwillSetdidSetdependentPropertyoriginalProperty

En outre, un contrôleur de vue dans une application macOS qui met à jour les contrôles dans sa vue à l'aide de liaisons Cocoa comme détail d'implémentation peut rendre certaines propriétés privées compatibles KVC afin de leur lier ces contrôles.

Donc, comme vous le voyez, il y a des moments où une méthode ou une propriété peut avoir besoin d'être exposée à Objective-C afin d'interagir avec les frameworks, sans nécessairement avoir besoin d'être visible pour les clients de votre code.

Charles Srstka
la source
25

@objc / dynamique

C'est pour la compatibilité: une fois que vous avez importé votre fichier / code Swift dans un projet basé sur Objective-C.

Et utilisez-le si vous voulez que votre propriété / méthode soit accessible par le code ou la classe Objective-C.

La plupart du temps, cela se produit lorsque vous sous-classez une classe Swift de la classe de base Objective-C.

Une classe ou un protocole Swift doit être marqué avec l' attribut @objc pour être accessible et utilisable en Objective-C. Cet attribut indique au compilateur que ce morceau de code Swift est accessible depuis Objective-C. Si votre classe Swift est un descendant d'une classe Objective-C, le compilateur ajoute automatiquement l'attribut @objc pour vous.

Voici la documentation Apple qui parle de @objc.

Utilisation de Swift depuis Objective-C

Compatibilité d'interopérabilité linguistique

Liens mis à jour: il
semble que les liens ont été mis à jour par Apple.

0yeoj
la source
11

@objc est un attribut de classe, vous utilisez donc

@objc public class MyClass

Il expose les méthodes de la classe aux classes Objective C, vous ne l'utiliserez donc que si votre classe contient des fonctions publiques

Fraser
la source
7

Une réponse tardive, mais ce @objccomportement est en train de changer légèrement à partir de Swift 4 (qui est sorti dans Xcode 9, qui était généralement publié il y a 10 jours).

Dans Swift 4, certains cas d'inférence de @objcsont supprimés. Cela signifie simplement que dans certains cas supplémentaires où avant que l'en- @objctête ait été déduit par le compilateur Swift, il n'est pas déduit dans Swift 4.

En savoir plus sur la proposition d'évolution Swift sur ce changement

Comme cela a été mentionné, en général @objcest d'exposer certaines méthodes au runtime Objective-C, qui fait partie de l'interopérabilité de Swift avec le langage.

BHendricks
la source
1
donc cela devrait être la raison pour laquelle, après la mise à jour vers Swift 4, certaines variables ne sont pas accessibles depuis ObjC, n'est-ce pas? Avec Swift 3.X, il n'était pas nécessaire d'ajouter@objc
rgkobashi
oh ok, il est toujours bon de vérifier avec quelqu'un d'autre, merci!
rgkobashi
1

@objcexpose une déclaration à Objective-C runtime. Jetons un coup d'œil à la #selectorfonctionnalité de Swift pour utiliser un runtime Objective-C. Dans ce cas, vous pouvez définir votre Swift @objc private func[Plus]

Pour utiliser les fonctions de Swift depuis Objective-C:

  1. La classe de Swift devrait être étendue de NSObject
  2. Mark Swift's:

    une. @objcMembers classe uniquement - pour exposer tous les public constructeurs , champs et méthodes . Il est également applicable pour les sous-classes

    b. @objc class / enum / protocol (sauf struct ) [Type nommé]

    • @objc class (facultatif) - pour exposer une valeur par défautpublic init() . Ou @objc(<custom_name>)pour configurer un nom personnalisé pour la classe.
    • @objc constructeurs , champs et méthodes - pour les exposer de manière sélective

La méthode de Swift sera disponible par le prochain nom:

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

Par exemple:

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

[ @objcet dynamic]

yoAlex5
la source