Clase base nativa Swift o NSObject

105

Probé un poco de isa swizzling con Swift, y descubrí que solo funciona cuando NSObject es una superclase (directamente o más arriba), o usando la decoración '@objc'. De lo contrario, seguirá un estilo de envío estático y vtable, como C ++.

¿Es normal definir una clase Swift sin una clase base Cocoa / NSObject? Si me preocupa, esto significa renunciar a gran parte del dinamismo de Objective-C, como la interceptación de métodos y la introspección en tiempo de ejecución.

El comportamiento dinámico en tiempo de ejecución se encuentra en el corazón de funciones como los observadores de propiedades, los datos centrales, la programación orientada a aspectos , la mensajería de orden superior , los marcos analíticos y de registro, etc.

El uso del estilo de invocación de métodos de Objective-C agrega alrededor de 20 operandos de código de máquina a una llamada de método, por lo que en ciertas situaciones ( muchas llamadas ajustadas a métodos con cuerpos pequeños ) el envío estático y vtable de estilo C ++ puede funcionar mejor.

Pero dada la regla general 95-5 (el 95% de las ganancias de rendimiento provienen de ajustar el 5% del código ), ¿no tiene sentido comenzar con las potentes funciones dinámicas y endurecerlas cuando sea necesario?

Jasper Blues
fuente
Relacionado: ¿Swift admite la programación orientada a aspectos? stackoverflow.com/a/24137487/404201
Jasper Blues

Respuestas:

109

Clases Swift que son subclases de NSObject:

  • son las clases de Objective-C en sí mismas
  • usar objc_msgSend()para llamadas a (la mayoría de) sus métodos
  • proporcionar metadatos en tiempo de ejecución de Objective-C para (la mayoría de) sus implementaciones de métodos

Clases Swift que no son subclases de NSObject:

  • son clases Objective-C, pero implementan solo un puñado de métodos para la compatibilidad con NSObject
  • no usar objc_msgSend()para llamadas a sus métodos (por defecto)
  • no proporcionan metadatos en tiempo de ejecución de Objective-C para las implementaciones de sus métodos (de forma predeterminada)

Subclasificar NSObject en Swift le brinda flexibilidad en el tiempo de ejecución de Objective-C pero también rendimiento de Objective-C. Evitar NSObject puede mejorar el rendimiento si no necesita la flexibilidad de Objective-C.

Editar:

Con Xcode 6 beta 6, aparece el atributo dinámico. Esto nos permite indicarle a Swift que un método debe utilizar el envío dinámico y, por lo tanto, admitirá la interceptación.

public dynamic func foobar() -> AnyObject {
}
Greg Parker
fuente
1
¿Qué tipo de envío utiliza Swift en lugar de objc_msgSend? ¿Es estático?
Proyecto de ley
12
Swift puede usar el envío objc_msgSend, el envío de tabla virtual, el envío directo o la inserción.
Greg Parker
estático: menos de 1,1 ns. vtable 1.1ns, msgSend 4.9ns. . (Depende del hardware, por supuesto). . Parece que Swift 'puro' es un gran lenguaje para la programación a nivel de sistema, pero para las aplicaciones, sería reacio a renunciar a las funciones dinámicas, aunque estaría feliz si se trasladaran al compilador como en Garbage Collection vs ARC. . . He oído decir que el envío estático permite una mejor predicción de ramas (y por lo tanto, el rendimiento) en sistemas de varios núcleos. ¿Cierto?
Jasper Blues
"Las clases Swift que no son subclases de NSObject son clases Objective-C". ¿Puede proporcionar un enlace al lugar donde encontró lo indicado?
Matt S.
1
Entonces, en resumen, ¿solo debería subclase NSObject en Swift si necesito comunicarme con el código Objective C?
Móvil el
14

También descubrí que si basaba una clase Swift en NSObject, veía un comportamiento inesperado en tiempo de ejecución que podía ocultar errores de codificación. Aquí hay un ejemplo.

En este ejemplo, donde no nos basamos en NSObject, el compilador detecta correctamente el error en testIncorrect_CompilerShouldSpot, informando "... 'MyClass' no es convertible a '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
      }
  }
}

En este ejemplo, donde nos basamos en NSObject , el compilador no detecta el error en 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
      }
  }
}

Supongo que la moraleja es, ¡solo base en NSObject donde realmente tienes que hacerlo!

Pete
fuente
1
Gracias por la info.
Jasper Blues
13

De acuerdo con la referencia del lenguaje, no hay ningún requisito para que las clases subclasifiquen ninguna clase raíz estándar, por lo que puede incluir u omitir una superclase según sea necesario.

Tenga en cuenta que omitir una superclase de la declaración de clase no asigna una superclase base implícita de ningún tipo. Define una clase base, que se convertirá efectivamente en la raíz de una jerarquía de clases independiente.

De la referencia del idioma:

Las clases rápidas no heredan de una clase base universal. Las clases que defina sin especificar una superclase se convertirán automáticamente en clases base sobre las que podrá construir.

Intentar hacer referencia superdesde una clase sin una superclase (es decir, una clase base) resultará en un error de tiempo de compilación

'super' members cannot be referenced in a root class
Cezar
fuente
1
Si. Si intenta crear un archivo fuente Cocoa o Cocoa Touch en el contexto de un proyecto, la forma en la que inserta la subclase en realidad le impide crear la clase mientras la deja en blanco. Puede eliminar la superclase más tarde, una vez que se haya creado.
Cezar
1
Gracias. El uso de la distribución estática y vtable tiene sentido para la programación de sistemas o el ajuste del rendimiento. Por coherencia con Cocoa nativo, creo que la dinámica debería ser la predeterminada. (A menos que alguien pueda convencerme de lo contrario, de ahí esta pregunta).
Jasper Blues
1

Creo que la gran mayoría de los datos de Swift no lo serán objc. Solo aquellas partes que necesiten comunicarse con la infraestructura del Objetivo C se marcarán explícitamente como tales.

Hasta qué punto se agregará la introspección en tiempo de ejecución al lenguaje, no lo sé. La interceptación de métodos probablemente solo sea posible si el método lo permite explícitamente. Esta es mi suposición, pero solo los diseñadores de lenguajes de Apple saben realmente hacia dónde se dirigen.

Archivo analógico
fuente
Para mí, tiene sentido hacer que el dinamismo sea el predeterminado (mediante la extensión de NSObject, utilizando la decoración '@objc' o algún otro enfoque). Parece que cuando no hay una clase base, Swift favorecerá el envío estático / vtable, que funciona mejor, pero en la mayoría de los casos esto no será necesario. . (El 90% de las ganancias provienen de ajustar el 10% del código). Mi esperanza es que la coherencia con el dinamismo de Objective-C sea optar por no participar en lugar de participar.
Jasper Blues
La forma en que está diseñado el idioma, es una suscripción opcional. Por lo que sabemos, es muy posible que en el futuro se agreguen API del sistema que no sean nativamente objc. A medio / largo plazo, incluso pueden migrar bibliotecas existentes a código no objc con una fina capa de compatibilidad en objc que llama al código no objc. Simplemente no lo sabemos. Probablemente seremos capaces de hacer educated guessesen unos años a partir de ahora. Pero por el momento es solo un gran signo de interrogación.
Archivo analógico
@JasperBlues Yo diría que el dinamismo no es necesario la mayor parte del tiempo y prefiero tener rendimiento por defecto y luego optar por el comportamiento dinámico que se necesita. Para cada uno lo suyo, supongo :)
Lance
@Lance Hmmmm. Tal vez. Todavía estoy preocupado por: observadores, AOP, marcos de prueba simulados, marcos analíticos, etc. y lo que pasa con el rendimiento es que el 90% de las ganancias provienen de un ajuste del 10%. . así que esa es mi razón fundamental: optar por esos casos del 10%. Creo que AOP será un gran problema para las aplicaciones empresariales de iOS, pero se podría hacer usando una herramienta de compilación como está en C ++. . ciertamente, los desarrolladores de juegos, informática científica, gráficos, etc. agradecerán más rendimiento :)
Jasper Blues
1

Lo siguiente es una copia del Swift-eBook de Apple y da una respuesta adecuada a su pregunta:

Definición de una clase base

Cualquier clase que no herede de otra clase se conoce como clase base.

Las clases rápidas no heredan de una clase base universal. Las clases que defina sin especificar una superclase se convertirán automáticamente en clases base sobre las que podrá construir.


Referencia

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

wottpal
fuente
-6

Es normal. Mire los objetivos de diseño de Swift: el objetivo es hacer desaparecer grandes clases de problemas de programación. El método swizzling probablemente no sea una de las cosas que quieras hacer con Swift.

gnasher729
fuente
3
La combinación de métodos es una forma de lograr el patrón de intercepción en tiempo de ejecución. Uno de los usos para esto es aplicar preocupaciones transversales de acuerdo con los principios de Programación Orientada a Aspectos. Las propias notificaciones de Apple, los observadores de propiedades (integradas en Swift), los datos centrales, todos usan swizzling con buenos resultados. . Una de las cosas que enamoró a la gente de Objective-C a pesar de su sintaxis torpe fue este dinamismo, que facilita la provisión de soluciones elegantes siempre que se requiera el patrón de intercepción. . de hecho, no había un marco AOP formal para Objc, porque las materias primas eran muy buenas.
Jasper Blues
El hecho de que así sea ahora no significa que así sea mañana. Como usted dice, las notificaciones, los observadores de la propiedad, los delegados y similares se proporcionan o pueden proporcionarse de forma nativa. El idioma evolucionará y no sabemos en qué dirección. Ha ganado mucho en términos de soporte para programación funcional. Pero lo hace de una manera inusual, así que no tengo ni idea de cuánto falta. Sin embargo, por lo que sabemos, es posible que se dirijan a desalentar la mutación y fomentar la programación funcional pura. ¿Y si el futuro de la IU es reactivo? No hay forma de saberlo todavía.
Archivo analógico
1
Sí, pero todas estas cosas dependen de la interceptación, que se puede hacer de dos maneras: tiempo de compilación (C ++) o tiempo de ejecución (Ruby, Objective-C, Java (a través de asm, cglib, ApsectJ, etc.)). Los lenguajes modernos tienden a hacerlo en tiempo de ejecución. A la gente le encantaba Objective-C, porque se comportaba como un lenguaje moderno a pesar de ser un viejo idiota. Como dices, cuanto más se incorpore en el idioma, mejor (¡siempre que se haga bien!). . pero por ahora podemos conectarnos con el legado proporcionado por Objc / Foundation, por lo que espero que extender NSObject se convierta en una práctica estándar para las aplicaciones diarias en los próximos años.
Jasper Blues