Schnelle native Basisklasse oder NSObject

105

Ich habe einige Isa getestet, die mit Swift sprudeln , und festgestellt, dass es nur funktioniert, wenn NSObject eine Superklasse ist (direkt oder weiter oben) oder indem ich die '@objc'-Dekoration verwende. Andernfalls folgt es einem statischen und vtable-Versandstil wie C ++.

Ist es normal, eine Swift-Klasse ohne eine Cocoa / NSObject-Basisklasse zu definieren? Wenn es mich betrifft, bedeutet dies, auf einen Großteil der Dynamik von Objective-C zu verzichten, wie z. B. das Abfangen von Methoden und die Introspektion zur Laufzeit.

Das dynamische Laufzeitverhalten steht im Mittelpunkt von Funktionen wie Eigenschaftsbeobachtern, Kerndaten, aspektorientierter Programmierung , Messaging höherer Ordnung , Analyse- und Protokollierungsframeworks usw.

Durch die Verwendung des Methodenaufrufstils von Objective-C werden einem Methodenaufruf etwa 20 Maschinencode-Operanden hinzugefügt , sodass in bestimmten Situationen ( viele enge Aufrufe von Methoden mit kleinen Körpern ) der statische und vtable-Versand im C ++ - Stil eine bessere Leistung erzielen kann.

Ist es angesichts der allgemeinen 95-5-Regel ( 95% der Leistungssteigerungen stammen aus der Optimierung von 5% des Codes ) nicht sinnvoll, mit den leistungsstarken dynamischen Funktionen zu beginnen und bei Bedarf zu härten?

Jasper Blues
quelle
Verwandte: Unterstützt Swift aspektorientierte Programmierung? stackoverflow.com/a/24137487/404201
Jasper Blues

Antworten:

109

Schnelle Klassen, die Unterklassen von NSObject sind:

  • sind Objective-C-Klassen selbst
  • Verwendung objc_msgSend()für Aufrufe (der meisten) ihrer Methoden
  • Bereitstellung von Objective-C-Laufzeitmetadaten für (die meisten) Methodenimplementierungen

Schnelle Klassen, die keine Unterklassen von NSObject sind:

  • sind Objective-C-Klassen, implementieren jedoch nur eine Handvoll Methoden für die NSObject-Kompatibilität
  • Nicht objc_msgSend()für Aufrufe ihrer Methoden verwenden (standardmäßig)
  • Geben Sie keine Objective-C-Laufzeitmetadaten für ihre Methodenimplementierungen an (standardmäßig).

Durch die Unterklasse von NSObject in Swift erhalten Sie Flexibilität bei der Objective-C-Laufzeit, aber auch bei der Objective-C-Leistung. Das Vermeiden von NSObject kann die Leistung verbessern, wenn Sie die Flexibilität von Objective-C nicht benötigen.

Bearbeiten:

Bei Xcode 6 Beta 6 wird das dynamische Attribut angezeigt. Auf diese Weise können wir Swift anweisen, dass eine Methode den dynamischen Versand verwenden soll, und daher das Abfangen unterstützen.

public dynamic func foobar() -> AnyObject {
}
Greg Parker
quelle
1
Welche Art von Versand verwendet Swift anstelle von objc_msgSend? Ist es statisch?
Bill
12
Swift kann objc_msgSend-Versand, Versand virtueller Tabellen, Direktversand oder Inlining verwenden.
Greg Parker
statisch: weniger als 1.1ns. vtable 1.1ns, msgSend 4.9ns. . (Hängt natürlich von der Hardware ab). . Es scheint, dass 'pure' Swift eine großartige Sprache für die Programmierung auf Systemebene ist, aber für Apps würde ich nur ungern auf dynamische Funktionen verzichten, obwohl ich mich freuen würde, wenn sie wie in Garbage Collection vs ARC zum Compiler wechseln würden. . . Ich habe gehört, dass der statische Versand eine bessere Verzweigungsvorhersage (und damit Leistung) für Mehrkernsysteme ermöglicht. Wahr?
Jasper Blues
"Swift-Klassen, die keine Unterklassen von NSObject sind, sind Objective-C-Klassen" - können Sie einen Link zu der Stelle bereitstellen, an der Sie diese angegeben haben?
Matt S.
1
Zusammenfassend sollte ich NSObject in Swift nur dann unterordnen, wenn ich mit Objective C-Code kommunizieren muss.
MobileMon
14

Ich fand auch heraus, dass ich, wenn ich eine Swift-Klasse auf NSObject basierte, ein unerwartetes Laufzeitverhalten sah, das Codierungsfehler verbergen konnte. Hier ist ein Beispiel.

In diesem Beispiel, in dem wir nicht auf NSObject basieren, erkennt der Compiler den Fehler in testIncorrect_CompilerShouldSpot korrekt und meldet "... 'MyClass' kann nicht in 'MirrorDisposition' konvertiert werden."

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
      }
  }
}

In diesem Beispiel , wo wir auf der Basis NSObject , der Compiler nicht den Fehler in testIncorrect_CompilerShouldSpot vor Ort:

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
      }
  }
}

Ich denke, die Moral ist, nur auf NSObject zu basieren, wo man wirklich muss!

Pete
quelle
1
Danke für die Information.
Jasper Blues
13

Gemäß der Sprachreferenz müssen Klassen keine Standardstammklasse unterklassifizieren, sodass Sie eine Superklasse nach Bedarf einschließen oder weglassen können.

Beachten Sie, dass das Weglassen einer Oberklasse in der Klassendeklaration keinerlei implizite Basis-Oberklasse zuweist. Es definiert eine Basisklasse, die effektiv zur Wurzel für eine unabhängige Klassenhierarchie wird.

Aus der Sprachreferenz:

Swift-Klassen erben nicht von einer universellen Basisklasse. Klassen, die Sie ohne Angabe einer Oberklasse definieren, werden automatisch zu Basisklassen, auf denen Sie aufbauen können.

Der Versuch, supervon einer Klasse ohne Superklasse (dh einer Basisklasse) zu referenzieren, führt zu einem Fehler bei der Kompilierung

'super' members cannot be referenced in a root class
Cezar
quelle
1
Ja. Wenn Sie versuchen, eine Cocoa- oder Cocoa Touch-Quelldatei im Kontext eines Projekts zu erstellen, verhindert das Formular, in das Sie die Unterklasse einfügen, tatsächlich, dass Sie die Klasse erstellen, während Sie sie leer lassen. Sie können die Oberklasse später entfernen, sobald sie erstellt wurde.
Cezar
1
Vielen Dank. Die Verwendung von statischem und vtable-Versand ist für die Systemprogrammierung oder Leistungsoptimierung sinnvoll. Aus Gründen der Konsistenz mit nativer Cocoa, ich glaube , dynamisch sollte die Standardeinstellung sein. (Es sei denn, jemand kann mich anders überzeugen, daher diese Frage).
Jasper Blues
1

Ich glaube, dass die überwiegende Mehrheit der Swift-Daten dies nicht sein wird objc. Nur diejenigen Teile, die mit der Objective C-Infrastruktur kommunizieren müssen, werden explizit als solche gekennzeichnet.

Inwieweit der Sprache eine Introspektion zur Laufzeit hinzugefügt wird, weiß ich nicht. Das Abfangen von Methoden wird wahrscheinlich nur möglich, wenn die Methode dies ausdrücklich zulässt. Dies ist meine Vermutung, aber nur die Sprachdesigner bei Apple wissen wirklich, wohin sie wirklich gehen.

Analoge Datei
quelle
Für mich ist es sinnvoll, Dynamik als Standard festzulegen (durch Erweitern von NSObject mithilfe der Dekoration '@objc' oder eines anderen Ansatzes). Es scheint, dass Swift, wenn es keine Basisklasse gibt, den statischen / vtable-Versand bevorzugt, der eine bessere Leistung erbringt, aber in den meisten Fällen ist dies nicht erforderlich. . (90% der Gewinne stammen aus der Optimierung von 10% des Codes). Ich hoffe, dass die Übereinstimmung mit der Dynamik von Objective-C eher ein Opt-out als ein Opt-in ist.
Jasper Blues
Die Art und Weise, wie die Sprache gestaltet ist, ist optional aktiviert. Soweit wir wissen, kann es durchaus sein, dass in Zukunft System-APIs hinzugefügt werden, die nicht nativ objektiv sind. Mittel- / langfristig können sie sogar vorhandene Bibliotheken auf Nicht-Objekt-Code mit einer dünnen Kompatibilitätsschicht in Objekt migrieren, die den Nicht-Objekt-Code aufruft. Wir wissen es einfach nicht. Wir werden es wahrscheinlich educated guessesin ein paar Jahren schaffen. Aber im Moment ist es nur ein großes Fragezeichen.
Analoge Datei
@JasperBlues Ich würde argumentieren, dass Dynamik die meiste Zeit nicht benötigt wird und ich lieber standardmäßig Leistung haben und mich dann für dynamisches Verhalten entscheiden möchte. Jedem sein eigenes, denke ich :)
Lance
@ Lance Hmmmm. Vielleicht. Ich mache mir immer noch Sorgen um: Beobachter, AOP, Test-Mocking-Frameworks, analytische Frameworks usw. und die Sache mit der Leistung ist, dass 90% der Gewinne aus der Abstimmung von 10% stammen. . Das ist meine Begründung - entscheiden Sie sich für diese 10% -Fälle. Ich denke, AOP wird eine große Sache für iOS-Unternehmensanwendungen sein, aber es könnte mit einem Compiler-Tool wie in C ++ durchgeführt werden. . sicherlich werden Spiele, wissenschaftliches Rechnen, Grafiken usw. Entwickler mehr Leistung begrüßen :)
Jasper Blues
1

Folgendes wurde aus Apples Swift-eBook kopiert und gibt eine angemessene Antwort auf Ihre Frage:

Basisklasse definieren

Jede Klasse, die nicht von einer anderen Klasse erbt, wird als Basisklasse bezeichnet.

Swift-Klassen erben nicht von einer universellen Basisklasse. Klassen, die Sie ohne Angabe einer Oberklasse definieren, werden automatisch zu Basisklassen, auf denen Sie aufbauen können.


Referenz

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

wottpal
quelle
-6

Es ist normal. Schauen Sie sich die Designziele von Swift an: Das Ziel ist es, große Klassen von Programmierproblemen verschwinden zu lassen. Methoden-Swizzling ist wahrscheinlich nicht eines der Dinge, die Sie mit Swift tun möchten.

gnasher729
quelle
3
Das Methoden-Swizzling ist ein Weg, um das Intercept-Muster zur Laufzeit zu erreichen. Eine der Verwendungsmöglichkeiten hierfür ist die Anwendung von Querschnittsthemen gemäß den Prinzipien der aspektorientierten Programmierung. Apples eigene Benachrichtigungen, Immobilienbeobachter (integriert, um schnell zu sein) und Kerndaten verwenden Swizzling effektiv. . Eines der Dinge, die die Leute trotz seiner klobigen Syntax in Objective-C verliebt haben, war diese Dynamik, die es einfach macht, elegante Lösungen bereitzustellen, wenn das Abfangmuster erforderlich ist. . Tatsächlich gab es keinen formalen AOP-Rahmen für Objc, weil die Rohstoffe so gut waren.
Jasper Blues
Die Tatsache, dass es jetzt so gemacht wird, bedeutet nicht, dass es morgen so gemacht wird. Wie Sie sagen, werden oder können Immobilienbeobachter, Delegierte und ähnliches nativ bereitgestellt werden. Die Sprache wird sich weiterentwickeln und wir wissen nicht, in welche Richtung. Es hat viel an Unterstützung für die funktionale Programmierung gewonnen. Aber es macht es auf ungewöhnliche Weise, so dass ich keine Ahnung habe, wie viel fehlt. Soweit wir wissen, können sie jedoch Mutationen entmutigen und eine reine funktionale Programmierung fördern. Was ist, wenn die Zukunft der Benutzeroberfläche reaktiv ist? Noch keine Möglichkeit, es zu wissen.
Analoge Datei
1
Ja, aber all diese Dinge hängen vom Abfangen ab, das auf zwei Arten erfolgen kann: Kompilierungszeit (C ++) oder Laufzeit (Ruby, Objective-C, Java (über asm, cglib, ApsectJ usw.)). Die modernen Sprachen neigen dazu, dies zur Laufzeit zu tun. Die Leute liebten Objective-C, weil es sich wie eine moderne Sprache verhielt, obwohl es ein alter Kodierer war. Wie Sie sagen, je mehr davon in die Sprache eingebrannt ist, desto besser (vorausgesetzt, es ist gut gemacht!). . aber jetzt können wir in das Erbe von ObjC / Foundation, sofern Haken, weshalb ich hoffe , erstreckt NSObject für die täglichen Anwendungen in den nächsten Jahren gängige Praxis wird.
Jasper Blues