Quando usar @objc no Swift?

87

No Swift, vejo alguns métodos como:

@objc private func doubleTapGestureRecognized(recognizer: UITapGestureRecognizer)

Eu queria saber quando usar @objc? Eu li alguns documentos, mas eles estão dizendo que quando você quiser que seja possível chamá-lo em Objective-C, você deve adicionar o sinalizador @objc

No entanto, esta é uma função privada no Swift, o que o @obj faz?

Wingzero
fonte
1
boa pergunta !!!
Erhan Demirci

Respostas:

67

privado significa que é visível apenas em Swift. então use @objc para ficar visível em Objective-C. Se você tem uma função para selecionar uma função privada no swift, ela é necessária.

O atributo @objc torna sua API Swift disponível em Objective-C e no tempo de execução Objective-C.

Consulte: 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
fonte
1
então @objc private func doubleTapGestureRecognized, de que adianta ter @objc e private? Você está dizendo que as classes Objective-C podem sobrescrever doubleTapGestureRecognized?
Wingzero
1
Eu acho, porque em obj-c você pode sobrescrever qualquer método de qualquer maneira.
horários estranhos
2
Não tenho certeza se esta resposta está certa e responde ao seu Q. Os ans fornecidos aqui já estão em seu próprio Q "mas eles estão dizendo que quando você quiser que seja chamado em Objective-C, você deve adicionar \ @objc flag", seu principal Q é "Esta é uma função privada no swift, o que o \ @obj faz?"
KBJ
3
Links quebrados. Especialmente na documentação da Apple. Considere extrair os principais elementos da documentação aqui.
levigroker
55

Outra resposta tardia, mas nenhuma das respostas existentes nesta pergunta realmente responde à pergunta do OP, que é: por que diabos você precisaria usar @objcem um privatemembro da classe, se @objcexiste para interação com Objective-C e o membro em questão é privado, o que significa que mesmo se você tiver o código Objective-C em seu projeto, ele não deverá ser capaz de ver o membro de qualquer maneira?

A razão é que, como muitos dos frameworks são escritos em Objective-C, às vezes os recursos do Objective-C são necessários para interagir com certas APIs.

Por exemplo, suponha que eu queira me registrar para receber uma notificação por meio de DistributedNotificationCenter:

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

Para que isso funcione, precisamos ser capazes de obter o seletor para o somethingHappenedmétodo. No entanto, os seletores são um conceito do Objective-C, portanto, se o método não for visível para o Objective-C, ele não terá um seletor. Portanto, mesmo que o método seja privado e não deva ser chamado por um código externo arbitrário, ele precisará de um @objcin para que o DistributedNotificationcódigo, que está escrito em Objective-C, seja capaz de chamá-lo por meio de seu seletor.

Outro caso comum em que @objcé necessário é oferecer suporte a Key-Value Coding (KVC), especialmente no macOS, onde KVC e KVO são usados ​​para implementar vinculações Cocoa. KVC é, como muitos outros sistemas em Cocoa, implementado em Objective-C, o que tem o efeito de exigir que as propriedades compatíveis com KVC sejam expostas ao tempo de execução Objective-C. Às vezes, faz sentido que as propriedades compatíveis com KVC sejam privadas. Um exemplo é quando você tem uma propriedade que afeta outras propriedades:

@objc private dynamic var originalProperty: String

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

Neste caso, a nossa propriedade real armazenado é privado, mas a propriedade dependente, o que nós não expor para o código fora, precisa enviar suas notificações quando a propriedade privada é atualizado. Ao marcar a propriedade privada como @objc, podemos fazer isso facilmente configurando uma dependência KVC - caso contrário, teríamos que escrever código para enviar manualmente as notificações na propriedade privada willSete nos didSetmanipuladores. Além disso, a propriedade estática que informa o sistema KVC de que dependentPropertydepende originalPropertyprecisa ser exposta ao Objective-C para que o sistema KVC o encontre e o chame, mas não é relevante para os clientes do nosso código.

Além disso, um controlador de visualização em um aplicativo macOS que atualiza os controles em sua visualização usando Cocoa Bindings como um detalhe de implementação pode tornar certas propriedades privadas compatíveis com KVC para vincular esses controles a elas.

Como você pode ver, há momentos em que um método ou propriedade pode precisar ser exposto ao Objective-C para interagir com os frameworks, sem necessariamente precisar estar visível para os clientes do seu código.

Charles Srstka
fonte
25

@objc / dynamic

É para compatibilidade: Depois de importar seu arquivo / código Swift para um projeto baseado em Objective-C.

E use-o se quiser que sua propriedade / método seja acessado pelo código ou classe Objective-C.

Na maioria das vezes, isso acontece quando você está subclassificando uma classe Swift da classe base Objective-C.

Uma classe ou protocolo Swift deve ser marcado com o atributo @objc para ser acessível e utilizável em Objective-C. Este atributo informa ao compilador que este pedaço de código Swift pode ser acessado a partir do Objective-C. Se sua classe Swift for descendente de uma classe Objective-C, o compilador adicionará automaticamente o atributo @objc para você.

Aqui a documentação da maçã que diz sobre @objc.

Usando Swift de Objective-C

Compatibilidade de interoperabilidade de linguagem

Links atualizados:
Parece que os links foram atualizados pela apple.

0yeoj
fonte
11

@objc é um atributo de classe, então você usa

@objc public class MyClass

Ele expõe os métodos da classe para classes Objective C, então você só vai usá-lo se sua classe contiver funções públicas

Fraser
fonte
7

Uma resposta tardia, mas esse @objccomportamento está mudando um pouco a partir do Swift 4 (que saiu no Xcode 9, que geralmente foi lançado há 10 dias).

No Swift 4, alguns casos de inferência de @objcsão removidos. Isso significa apenas que, em alguns casos adicionais, onde antes do @objccabeçalho ser inferido pelo compilador Swift, ele não foi inferido no Swift 4.

Leia mais na proposta de evolução do Swift sobre essa mudança

Como foi mencionado, em geral @objcé expor certos métodos para o tempo de execução do Objective-C, que faz parte da interoperabilidade da linguagem do Swift.

BHendricks
fonte
1
então esta deve ser a razão pela qual após atualizar para o Swift 4 algumas variáveis ​​não estão acessíveis no ObjC certo? Com o Swift 3.X, não houve necessidade de adicionar@objc
rgkobashi
oh ok, é sempre bom verificar com outra pessoa, obrigado!
rgkobashi
1

@objcexpõe uma declaração para Objective-C runtime. Vamos dar uma olhada no #selectorrecurso do Swift para usar um tempo de execução Objective-C. Neste caso, você pode definir o seu Swift @objc private func[Mais]

Para usar as funções do Swift em Objective-C:

  1. A aula de Swift deve ser estendida de NSObject
  2. Mark Swift's:

    uma. somente @objcMembers classe - para expor todos os public construtores , campos e métodos . Também é aplicável para subclasses

    b. @objc classe / enum / protocolo (exceto estrutura ) [Tipo Nomeado]

    • @objc classe (opcional) - para expor um padrão public init() . Ou @objc(<custom_name>)para configurar um nome personalizado para a classe.
    • @objc construtores , campos e métodos - para expô-los seletivamente

O método do Swift estará disponível na próxima nomenclatura:

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

Por exemplo:

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

[ @objce dynamic]

yoAlex5
fonte