Classe base nativa Swift ou NSObject

105

Eu testei alguns isa swizzling com Swift e descobri que só funciona quando NSObject é uma superclasse (diretamente ou mais acima), ou usando a decoração '@objc'. Caso contrário, ele seguirá um estilo de envio estático e vtable, como C ++.

É normal definir uma classe Swift sem uma classe base Cocoa / NSObject? Se estou preocupado, isso significa renunciar a muito do dinamismo do Objective-C, como a interceptação de método e a introspecção em tempo de execução.

O comportamento dinâmico em tempo de execução está no centro de recursos como observadores de propriedade, dados centrais, programação orientada a aspectos , mensagens de ordem superior , estruturas analíticas e de registro e assim por diante.

Usar o estilo de invocação de método de Objective-C adiciona cerca de 20 operandos de código de máquina a uma chamada de método, portanto, em certas situações ( muitas chamadas apertadas para métodos com corpos pequenos ), o despacho estático e vtable do estilo C ++ pode ter um desempenho melhor.

Mas dada a regra geral 95-5 ( 95% dos ganhos de desempenho vêm do ajuste de 5% do código ), não faz sentido começar com os poderosos recursos dinâmicos e endurecer quando necessário?

Jasper Blues
fonte
Relacionado: O Swift suporta programação orientada a aspectos? stackoverflow.com/a/24137487/404201
Jasper Blues

Respostas:

109

Classes Swift que são subclasses de NSObject:

  • são as próprias classes Objective-C
  • usar objc_msgSend()para chamadas para (a maioria) seus métodos
  • fornecem metadados de tempo de execução Objective-C para (a maioria) suas implementações de método

Classes Swift que não são subclasses de NSObject:

  • são classes Objective-C, mas implementam apenas alguns métodos para compatibilidade NSObject
  • não use objc_msgSend()para chamadas para seus métodos (por padrão)
  • não fornecem metadados de tempo de execução Objective-C para suas implementações de método (por padrão)

Subclassificar NSObject em Swift proporciona flexibilidade de tempo de execução Objective-C, mas também desempenho Objective-C. Evitar NSObject pode melhorar o desempenho se você não precisar da flexibilidade de Objective-C.

Editar:

Com o Xcode 6 beta 6, o atributo dinâmico aparece. Isso nos permite instruir o Swift que um método deve usar despacho dinâmico e, portanto, oferecerá suporte à interceptação.

public dynamic func foobar() -> AnyObject {
}
Greg Parker
fonte
1
Que tipo de envio o Swift usa em vez de objc_msgSend? É estático?
Bill
12
O Swift pode usar envio objc_msgSend, envio de tabela virtual, envio direto ou inlining.
Greg Parker
estático: menos de 1,1 ns. vtable 1.1ns, msgSend 4.9ns. . (Depende do hardware, é claro). . Parece que o Swift 'puro' é uma ótima linguagem para programação em nível de sistema, mas para aplicativos, eu relutaria em abrir mão dos recursos dinâmicos, embora ficaria feliz se eles mudassem para o compilador como em Garbage Collection vs ARC. . . Ouvi dizer que o despacho estático permite uma melhor previsão de branch (e, portanto, desempenho) em sistemas multi-core. Verdade?
Jasper Blues
"As classes Swift que não são subclasses de NSObject são classes Objective-C" - você pode fornecer um link para onde você encontrou isso?
Matt S.
1
Portanto, em resumo, só devo criar uma subclasse de NSObject em Swift se precisar me comunicar com o código Objective C?
MobileMon
14

Também descobri que, ao basear uma classe Swift em NSObject, vi alguns comportamentos inesperados em tempo de execução que podem ocultar bugs de codificação. Aqui está um exemplo.

Neste exemplo, onde não baseamos em NSObject, o compilador identifica corretamente o erro em testIncorrect_CompilerShouldSpot, relatando "... 'MyClass' não é conversível para 'MirrorDisposition'"

class MyClass {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

Neste exemplo, onde nos baseamos em NSObject , o compilador não detecta o erro em testIncorrect_CompilerShouldSpot:

class myClass : NSObject {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

Eu acho que a moral é, apenas baseie-se no NSObject onde você realmente precisa!

Pete
fonte
1
Obrigado pela informação.
Jasper Blues
13

De acordo com a referência da linguagem, não há requisitos para classes de subclasse de qualquer classe raiz padrão, portanto, você pode incluir ou omitir uma superclasse conforme necessário.

Observe que omitir uma superclasse da declaração da classe não atribui uma superclasse de base implícita de qualquer tipo. Ele define uma classe base, que se tornará efetivamente a raiz de uma hierarquia de classes independente.

Da referência de idioma:

As classes Swift não herdam de uma classe base universal. As classes que você define sem especificar uma superclasse tornam-se automaticamente classes básicas para você construir.

Tentar fazer referência a superpartir de uma classe sem uma superclasse (ou seja, uma classe base) resultará em um erro de tempo de compilação

'super' members cannot be referenced in a root class
Cezar
fonte
1
Sim. Se você tentar criar um arquivo de origem Cocoa Or Cocoa Touch no contexto de um projeto, a forma em que você insere a subclasse realmente impede que você crie a classe, deixando-a em branco. Você pode remover a superclasse posteriormente, uma vez que ela foi criada.
Cezar
1
Obrigado. O uso de despacho estático e vtable faz sentido para programação de sistemas ou ajuste de desempenho. Para consistência com Cocoa nativo, acho que dinâmico deve ser o padrão. (A menos que alguém possa me convencer do contrário, daí esta questão).
Jasper Blues
1

Acredito que a grande maioria dos dados do Swift não será objc. Apenas as partes que precisam se comunicar com a infraestrutura Objective C serão marcadas explicitamente como tal.

Até que ponto a introspecção em tempo de execução será adicionada à linguagem, não sei. A interceptação do método provavelmente só será possível se o método permitir explicitamente. Este é o meu palpite, mas apenas os designers de linguagem da Apple realmente sabem para onde estão realmente indo.

Arquivo Analógico
fonte
Para mim, faz sentido tornar o dinamismo o padrão (por meio da extensão de NSObject, usando a decoração '@objc' ou alguma outra abordagem). Parece que, quando não há uma classe base, o Swift favorece o envio estático / vtable, que tem um desempenho melhor, mas na maioria dos casos isso não será necessário. . (90% dos ganhos vêm do ajuste de 10% do código). Minha esperança é que a consistência com o dinamismo do Objective-C seja opt-out em vez de opt-in.
Jasper Blues
A forma como a linguagem é projetada, é opcional. Tanto quanto sabemos, pode muito bem ser que no futuro sejam adicionadas APIs de sistema que não sejam nativamente objc. No médio / longo prazo, eles podem até mesmo migrar as bibliotecas existentes para o código não objc com uma camada de compatibilidade fina no objc que chama o código não objc. Nós simplesmente não sabemos. Provavelmente seremos capazes de fazer educated guessesem alguns anos a partir de agora. Mas, por enquanto, é apenas um grande ponto de interrogação.
Arquivo analógico de
@JasperBlues Eu diria que o dinamismo não é necessário na maioria das vezes e prefiro ter desempenho por padrão e, em seguida, optar pelo comportamento dinâmico necessário. Acho que para cada um :)
Lance
@Lance Hmmmm. Talvez. Ainda estou preocupado com: observadores, AOP, frameworks de simulação de teste, frameworks analíticos, etc. e a questão do desempenho é que 90% dos ganhos vêm do ajuste de 10%. . então esse é o meu raciocínio - optar por esses casos de 10%. Acho que AOP será um grande negócio para aplicativos iOS corporativos, mas pode ser feito usando uma ferramenta de compilador, pois é em C ++. . certamente jogos, computação científica, gráficos, etc, devs darão as boas-vindas a mais desempenho :)
Jasper Blues
1

O seguinte foi copiado do Swift-eBook da Apple e dá uma resposta apropriada à sua pergunta:

Definindo uma classe-base

Qualquer classe que não herde de outra classe é conhecida como classe base.

As classes Swift não herdam de uma classe base universal. As classes que você define sem especificar uma superclasse tornam-se automaticamente classes básicas para você construir.


Referência

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_251

wottpal
fonte
-6

É normal. Veja os objetivos de design do Swift: O objetivo é fazer desaparecer grandes classes de problemas de programação. Swizzling de métodos provavelmente não é uma das coisas que você deseja fazer com o Swift.

gnasher729
fonte
3
Swizzling de método é uma maneira de atingir o padrão de interceptação em tempo de execução. Um dos usos para isso é aplicar interesses transversais de acordo com os princípios da Programação Orientada a Aspectos. As próprias notificações da Apple, observadores de propriedade (integrados ao Swift) e dados básicos usam o swizzling com bons resultados. . Uma das coisas que encantou as pessoas por Objective-C, apesar de sua sintaxe desajeitada, foi esse dinamismo, que torna fácil fornecer soluções elegantes sempre que o padrão de interceptação é necessário. . na verdade, não havia uma estrutura AOP formal para Objc, porque as matérias-primas eram muito boas.
Jasper Blues
O fato de ser assim agora não significa que será assim amanhã. Como você disse, notificações, observadores de propriedade, delegados e similares são ou podem ser fornecidos nativamente. A linguagem vai evoluir e não sabemos em que direção. Ganhou muito em termos de suporte para programação funcional. Mas ele faz isso de uma maneira incomum, então não tenho ideia de quanto está faltando. No entanto, até onde sabemos, eles podem estar caminhando para desencorajar a mutação e encorajar a programação funcional pura. E se o futuro da IU for reativo? Não há como saber ainda.
Arquivo analógico de
1
Sim, mas todas essas coisas dependem de interceptação, que pode ser feita de duas formas: tempo de compilação (C ++) ou tempo de execução (Ruby, Objective-C, Java (via asm, cglib, ApsectJ, etc)). As linguagens modernas tendem a fazer isso em tempo de execução. As pessoas adoravam Objective-C, porque se comportava como uma linguagem moderna, apesar de ser um velho codger. Como você disse, quanto mais disso for incorporado à linguagem, melhor (desde que bem feito!). . mas, por enquanto , podemos nos conectar com o legado fornecido pela Objc / Foundation, e é por isso que espero que estender o NSObject se torne uma prática padrão para aplicativos do dia a dia nos próximos anos.
Jasper Blues