Warum gibt es in Swift zwei Gleichheitszeichen und was verbergen sie?
\n\nHaben Sie jemals in Swift programmiert und sind plötzlich auf den seltsamen Operator === gestoßen? Der erste Gedanke: "Tippfehler? Oder ist das eine Art Super-Gleich?" Tatsächlich ist es kein Tippfehler, sondern ein mächtiger Mechanismus, der die Vergleichslogik grundlegend ändert. Wenn Sie mit Klassen, Strukturen oder Closures arbeiten, ist das Verständnis des Unterschieds zwischen == und === keine Theorie, sondern eine Notwendigkeit. Ohne dieses Wissen riskieren Sie schwer zu findende Fehler.
In diesem Artikel werfen wir einen Blick "unter die Haube" von Swift, analysieren, wie beide Operatoren funktionieren, und lernen, das richtige Werkzeug für jede Aufgabe auszuwählen. Bereit? Los geht's.
\n\nWas ist der Gleichheitsoperator ==?
\n\nDer Operator == ist Ihr Standard-"Freund" zum Vergleichen von Werten. Er prüft, ob zwei Objekte inhaltlich gleich sind. Aber es gibt einen Haken: Swift weiß nicht, was "gleich" für Ihren eigenen Datentyp bedeutet. Um == verwenden zu können, muss Ihr Typ daher dem Protokoll Equatable entsprechen.
So sieht das für eingebaute Typen aus:
\n\nlet a = 5\nlet b = 5\nprint(a == b) // true, weil die Werte gleich sind\n\nlet name1 = "Alice"\nlet name2 = "Alice"\nprint(name1 == name2) // true, die Zeichenketten sind gleich\n\nFür Klassen und Strukturen müssen Sie Equatable selbst implementieren. Zum Beispiel:
struct User {\n let id: Int\n let name: String\n}\n\nextension User: Equatable {\n static func == (lhs: User, rhs: User) -> Bool {\n return lhs.id == rhs.id && lhs.name == rhs.name\n }\n}\n\nlet user1 = User(id: 1, name: "John")\nlet user2 = User(id: 1, name: "John")\nprint(user1 == user2) // true, der Inhalt stimmt überein\n\nBeachten Sie: == vergleicht Werte, nicht Referenzen. Bei Strukturen ist das offensichtlich, aber bei Klassen wird es richtig interessant.
Der Identitätsoperator ===: Wenn zwei Objekte ein und dasselbe sind
\n\nDer Operator === (dreifaches Gleichheitszeichen) prüft nicht den Inhalt, sondern die Identität von Referenzen. Er beantwortet die Frage: "Zeigen zwei Variablen auf dasselbe Objekt im Speicher?"
Dieser Operator funktioniert nur mit Referenztypen: Klassen und Closures (Closures sind ebenfalls Referenztypen). Für Strukturen und Aufzählungen existiert er schlichtweg nicht – der Compiler gibt einen Fehler aus.
\n\nSehen Sie sich das Beispiel an:
\n\nclass Car {\n var model: String\n init(model: String) {\n self.model = model\n }\n}\n\nlet car1 = Car(model: "Tesla")\nlet car2 = Car(model: "Tesla")\nlet car3 = car1\n\nprint(car1 == car2) // false, wenn Equatable nicht implementiert ist\nprint(car1 === car2) // false, unterschiedliche Objekte\nprint(car1 === car3) // true, dieselbe Referenz\n\nHier sind car1 und car2 zwei verschiedene Objekte mit demselben Inhalt. car3 ist hingegen nur ein weiterer Name für dasselbe Objekt wie car1. === zeigt dies deutlich.
Wie es unter der Haube funktioniert: Speicher und Zeiger
\n\nUm den Unterschied zu verstehen, müssen wir einen Blick in den Speicher werfen. Wenn Sie eine Instanz einer Klasse erstellen, reserviert Swift einen Bereich im Heap (Haldenspeicher) und gibt einen Zeiger auf diesen Bereich zurück. Die Variable speichert genau diesen Zeiger, nicht das Objekt selbst.
\n\n- \n
==vergleicht den Inhalt über den Zeiger: Es ruft Ihre Implementierung vonEquatableauf, die auf die Felder des Objekts schauen kann. \n ===vergleicht die Zeiger selbst: Wenn zwei Zeiger auf dieselbe Adresse im Speicher zeigen, ist das Ergebnistrue. \n
Bei Strukturen ist es einfacher: Sie werden auf dem Stack (Stapelspeicher) gespeichert und bei der Zuweisung kopiert. Daher macht === für sie keinen Sinn – zwei verschiedene Strukturen sind immer unterschiedliche Objekte im Speicher, selbst wenn ihr Inhalt identisch ist.