Classe de base native Swift ou NSObject

105

J'ai testé certaines isa swizzling avec Swift, et j'ai trouvé que cela ne fonctionne que lorsque NSObject est une super-classe (directement ou plus haut), ou en utilisant la décoration '@objc'. Sinon, il suivra un style de distribution statique et vtable, comme C ++.

Est-il normal de définir une classe Swift sans classe de base Cocoa / NSObject? Si cela me préoccupe, cela signifie renoncer à une grande partie du dynamisme d'Objective-C, comme l'interception de méthodes et l'introspection à l'exécution.

Le comportement dynamique à l'exécution est au cœur de fonctionnalités telles que les observateurs de propriétés, les données de base, la programmation orientée aspect , la messagerie d'ordre supérieur , les cadres d'analyse et de journalisation, etc.

L'utilisation du style d'appel de méthode d'Objective-C ajoute environ 20 opérandes de code machine à un appel de méthode, donc dans certaines situations ( beaucoup d'appels serrés à des méthodes avec de petits corps ), la distribution statique et vtable de style C ++ peut être meilleure.

Mais étant donné la règle générale des 95-5 ( 95% des gains de performances proviennent du réglage de 5% du code ), n'est-il pas logique de commencer par les puissantes fonctionnalités dynamiques et de les renforcer si nécessaire?

Jasper Blues
la source
Connexes: Swift prend-il en charge la programmation orientée aspect? stackoverflow.com/a/24137487/404201
Jasper Blues

Réponses:

109

Classes Swift qui sont des sous-classes de NSObject:

  • sont des classes Objective-C elles-mêmes
  • utiliser objc_msgSend()pour les appels à (la plupart de) leurs méthodes
  • fournir des métadonnées d'exécution Objective-C pour (la plupart de) leurs implémentations de méthodes

Classes Swift qui ne sont pas des sous-classes de NSObject:

  • sont des classes Objective-C, mais implémentent seulement une poignée de méthodes pour la compatibilité NSObject
  • ne pas utiliser objc_msgSend()pour les appels à leurs méthodes (par défaut)
  • ne fournissent pas de métadonnées d'exécution Objective-C pour leurs implémentations de méthode (par défaut)

Le sous-classement de NSObject dans Swift vous offre une flexibilité d'exécution Objective-C mais également des performances Objective-C. Éviter NSObject peut améliorer les performances si vous n'avez pas besoin de la flexibilité d'Objective-C.

Éditer:

Avec Xcode 6 beta 6, l'attribut dynamique apparaît. Cela nous permet d'indiquer à Swift qu'une méthode doit utiliser la répartition dynamique et prendra donc en charge l'interception.

public dynamic func foobar() -> AnyObject {
}
Greg Parker
la source
1
Quel type de répartition Swift utilise-t-il au lieu de objc_msgSend? Est-ce statique?
Projet de loi le
12
Swift peut utiliser la distribution objc_msgSend, la distribution de table virtuelle, la distribution directe ou l'inlining.
Greg Parker
statique: moins de 1,1 ns. vtable 1.1ns, msgSend 4.9ns. . (Dépend du matériel bien sûr). . Il semble `` pur '' Swift est un excellent langage pour la programmation au niveau du système, mais pour les applications, je serais réticent à renoncer aux fonctionnalités dynamiques, même si je serais heureux s'ils passaient au compilateur comme dans Garbage Collection vs ARC. . . J'ai entendu dire que la répartition statique permet une meilleure prédiction de branche (et donc des performances) sur les systèmes multicœurs. Vrai?
Jasper Blues
"Les classes Swift qui ne sont pas des sous-classes de NSObject sont des classes Objective-C" - pouvez-vous fournir un lien vers où vous avez trouvé cela indiqué?
Matt S.
1
Donc, en résumé, je ne devrais sous-classer NSObject dans Swift que si j'ai besoin de communiquer avec du code Objective C?
MobileMon
14

J'ai également constaté que si je basais une classe Swift sur NSObject, je voyais un comportement d'exécution inattendu qui pouvait masquer des bogues de codage. Voici un exemple.

Dans cet exemple, où nous ne nous basons pas sur NSObject, le compilateur repère correctement l'erreur dans testIncorrect_CompilerShouldSpot, signalant "... 'MyClass' n'est pas convertible en '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
      }
  }
}

Dans cet exemple, où nous nous basons sur NSObject , le compilateur ne détecte pas l'erreur dans 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
      }
  }
}

Je suppose que la morale est, uniquement basée sur NSObject où vous devez vraiment le faire!

Pete
la source
1
Merci pour l'info.
Jasper Blues
13

Selon la référence du langage, il n'est pas nécessaire que les classes sous-classent une classe racine standard, vous pouvez donc inclure ou omettre une superclasse si nécessaire.

Notez que l'omission d'une superclasse de la déclaration de classe n'affecte pas une superclasse de base implicite de quelque sorte que ce soit. Il définit une classe de base, qui deviendra effectivement la racine d'une hiérarchie de classes indépendante.

À partir de la référence de langue:

Les classes Swift n'héritent pas d'une classe de base universelle. Les classes que vous définissez sans spécifier de superclasse deviennent automatiquement des classes de base sur lesquelles vous pouvez vous appuyer.

Essayer de faire référence à superpartir d'une classe sans super classe (c'est-à-dire une classe de base) entraînera une erreur de compilation

'super' members cannot be referenced in a root class
Cezar
la source
1
Ouais. Si vous essayez de créer un fichier source Cocoa ou Cocoa Touch dans le contexte d'un projet, la forme dans laquelle vous insérez la sous-classe vous empêche en fait de créer la classe tout en la laissant vide. Vous pouvez supprimer la superclasse plus tard une fois qu'elle a été créée.
Cezar
1
Merci. L'utilisation de la répartition statique et de la table virtuelle est logique pour la programmation des systèmes ou le réglage des performances. Par souci de cohérence avec Cocoa natif, je pense que dynamique devrait être la valeur par défaut. (À moins que quelqu'un ne puisse me convaincre du contraire, d'où cette question).
Jasper Blues
1

Je pense que la grande majorité des données Swift ne seront pas objc . Seules les parties qui doivent communiquer avec l'infrastructure de l'Objectif C seront explicitement marquées comme telles.

Dans quelle mesure l'introspection d'exécution sera ajoutée au langage, je ne sais pas. L'interception de méthode ne deviendra probablement possible que si la méthode le permet explicitement. C'est mon hypothèse, mais seuls les concepteurs de langage chez Apple savent vraiment où ils se dirigent vraiment.

Fichier analogique
la source
Pour moi, il est logique de faire du dynamisme la valeur par défaut (en étendant NSObject, en utilisant la décoration '@objc' ou une autre approche). Il semble que lorsqu'il n'y a pas de classe de base, Swift favorisera la distribution statique / vtable, qui fonctionne mieux, mais dans la plupart des cas, cela ne sera pas nécessaire. . (90% des gains proviennent du réglage de 10% du code). J'espère que la cohérence avec le dynamisme d'Objective-C est un opt-out plutôt qu'un opt-in.
Jasper Blues
La façon dont le langage est conçu est optionnel. Autant que nous le sachions, il se peut très bien que dans le futur des API système soient ajoutées qui ne sont pas nativement objc. À moyen / long terme, ils peuvent même migrer des bibliothèques existantes vers du code non objc avec une fine couche de compatibilité dans objc qui appelle le code non objc. Nous ne savons pas. Nous pourrons probablement faire educated guessesdans quelques années à partir de maintenant. Mais pour le moment, ce n'est qu'un gros point d'interrogation.
Fichier analogique
@JasperBlues Je dirais que le dynamisme n'est pas nécessaire la plupart du temps et que je préfère avoir des performances par défaut, puis opter pour le comportement dynamique nécessaire. À chacun le sien, je suppose :)
Lance
@Lance Hmmmm. Peut être. Je suis toujours préoccupé par: les observateurs, les AOP, les frameworks de simulation de test, les frameworks analytiques, etc. et le problème de la performance est que 90% des gains proviennent du réglage de 10%. . c'est donc ma justification - opter pour ces cas de 10%. Je pense que AOP sera un gros problème pour les applications d'entreprise iOS, mais cela pourrait être fait en utilisant un outil de compilation tel qu'il est en C ++. . certainement les développeurs de jeux, de calcul scientifique, de graphisme, etc. accueilleront plus de performances :)
Jasper Blues
1

Ce qui suit est copié du Swift-eBook d'Apple et donne une réponse appropriée à votre question:

Définition d'une classe de base

Toute classe qui n'hérite pas d'une autre classe est appelée classe de base.

Les classes Swift n'héritent pas d'une classe de base universelle. Les classes que vous définissez sans spécifier de superclasse deviennent automatiquement des classes de base sur lesquelles vous pouvez vous appuyer.


Référence

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

wottpal
la source
-6

C'est normal. Regardez les objectifs de conception de Swift: le but est de faire disparaître d'énormes classes de problèmes de programmation. Le swizzling de méthode n'est probablement pas l'une des choses que vous voulez faire avec Swift.

gnasher729
la source
3
Le swizzling de méthode est un moyen d'obtenir le modèle d'interception au moment de l'exécution. L'une des utilisations pour cela est d'appliquer des préoccupations transversales selon les principes de la programmation orientée aspect. Les propres notifications d'Apple, les observateurs de propriétés (intégrés à Swift), les données de base utilisent tous le swizzling à bon escient. . L'une des choses qui a séduit les gens d'Objective-C malgré sa syntaxe maladroite était ce dynamisme, qui permet de fournir facilement des solutions élégantes chaque fois que le modèle d'interception est requis. . en fait, il n'y avait pas de cadre AOP formel pour Objc, car les matières premières étaient si bonnes.
Jasper Blues
Le fait que c'est ainsi que cela se fait maintenant ne signifie pas que c'est ainsi que cela se fera demain. Comme vous le dites, les notifications, les observateurs de propriété, les délégués et autres sont ou peuvent être fournis de manière native. La langue va évoluer, et on ne sait pas dans quelle direction. Il a beaucoup gagné en termes de support pour la programmation fonctionnelle. Mais il le fait d'une manière inhabituelle, donc je n'ai aucune idée de combien il en manque. Pourtant, pour autant que nous le sachions, ils pourraient se diriger vers la découragement de la mutation et la promotion de la programmation fonctionnelle pure. Et si l'avenir de l'interface utilisateur était réactif? Pas moyen de savoir encore.
Fichier analogique
1
Oui, mais tout cela dépend de l'interception, qui peut être effectuée de deux manières: à la compilation (C ++) ou à l'exécution (Ruby, Objective-C, Java (via asm, cglib, ApsectJ, etc.)). Les langues modernes ont tendance à le faire au moment de l'exécution. Les gens adoraient Objective-C, car il se comportait comme un langage moderne en dépit d'être un vieux codeur. Comme vous le dites, plus cela est intégré à la langue, mieux c'est (à condition que ce soit bien fait!). . mais pour l'instant, nous pouvons nous connecter à l'héritage fourni par Objc / Foundation, c'est pourquoi j'espère que l'extension de NSObject deviendra une pratique standard pour les applications quotidiennes au cours des prochaines années.
Jasper Blues