Unterschied zwischen == und ===


300

In Swift scheint es zwei Gleichheitsoperatoren zu geben: die doppelten Gleichen ( ==) und die dreifachen Gleichen ( ===). Was ist der Unterschied zwischen den beiden?

Antworten:


149

Zusamenfassend:

== Operator prüft, ob ihre Instanzwerte gleich sind, "equal to"

=== Der Operator prüft, ob die Referenzen auf dieselbe Instanz verweisen. "identical to"

Lange Antwort:

Klassen sind Referenztypen. Es ist möglich, dass mehrere Konstanten und Variablen hinter den Kulissen auf dieselbe einzelne Instanz einer Klasse verweisen. Klassenreferenzen verbleiben im Run Time Stack (RTS) und ihre Instanzen verbleiben im Heap-Bereich des Speichers. Wenn Sie die Gleichheit ==damit steuern , bedeutet dies, dass ihre Instanzen gleich sind. Es muss nicht dieselbe Instanz sein, um gleich zu sein. Dazu müssen Sie Ihrer benutzerdefinierten Klasse ein Gleichheitskriterium bereitstellen. Standardmäßig erhalten benutzerdefinierte Klassen und Strukturen keine Standardimplementierung der Äquivalenzoperatoren, die als Operator "gleich" ==und "ungleich" bezeichnet werden !=. Dazu muss Ihre benutzerdefinierte Klasse dem EquatableProtokoll und seiner static func == (lhs:, rhs:) -> BoolFunktion entsprechen

Schauen wir uns ein Beispiel an:

class Person : Equatable {
    let ssn: Int
    let name: String

    init(ssn: Int, name: String) {
        self.ssn = ssn
        self.name = name
    }

    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.ssn == rhs.ssn
    }
}

P.S.: Da ssn (Sozialversicherungsnummer) eine eindeutige Nummer ist, müssen Sie nicht vergleichen, ob ihr Name gleich ist oder nicht.

let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")

if person1 == person2 {
   print("the two instances are equal!")
}

Obwohl die Referenzen person1 und person2 auf zwei verschiedene Instanzen im Heap-Bereich verweisen, sind ihre Instanzen gleich, da ihre ssn-Nummern gleich sind. So wird die Ausgabe seinthe two instance are equal!

if person1 === person2 {
   //It does not enter here
} else {
   print("the two instances are not identical!")
}

===Der Operator prüft, ob die Referenzen auf dieselbe Instanz verweisen "identical to". Da person1 und person2 im Heap-Bereich zwei unterschiedliche Instanzen haben, sind sie und die Ausgabe nicht identischthe two instance are not identical!

let person3 = person1

P.S: Klassen sind Referenztypen, und die Referenz von Person1 wird mit dieser Zuweisungsoperation auf Person3 kopiert. Daher verweisen beide Referenzen auf dieselbe Instanz im Heap-Bereich.

if person3 === person1 {
   print("the two instances are identical!")
}

Sie sind identisch und die Ausgabe wird sein the two instances are identical!


248

!== und === sind Identitätsoperatoren und werden verwendet, um zu bestimmen, ob zwei Objekte dieselbe Referenz haben.

Swift bietet außerdem zwei Identitätsoperatoren (=== und! ==), mit denen Sie testen können, ob zwei Objektreferenzen beide auf dieselbe Objektinstanz verweisen.

Auszug aus: Apple Inc. "Die schnelle Programmiersprache". iBooks. https://itun.es/us/jEUH0.l


49
Jep. Von ObjC kommend, ==ist isEqual:oder klassendefinierte semantische Äquivalenz. ===in Swift ist ==in (Obj) C - Zeigergleichheit oder Objektidentität.
Rickster

@rickster Haben die Werte nicht auch einen Speicherort? Ich bin schließlich, dass sie irgendwo in der Erinnerung sind. Kannst du die nie vergleichen? Oder bietet ihr Speicherort keinen aussagekräftigen Wert?
Honig

2
Es gibt mindestens zwei Möglichkeiten, darüber nachzudenken, wie die Sprache Werttypen gegenüber dem Speicher definiert. Zum einen ist jede Bindung ( varoder let) eines Namens an einen Wert eine eindeutige Kopie. Daher ist es sinnlos, Zeiger zu erstellen, da der Wert, auf den Sie einen Zeiger erstellt haben, ein anderer Wert ist als der, den Sie zuerst erstellt haben. Ein weiterer Grund ist, dass Swifts Definition der Wertesemantik den Speicher abstrahiert - der Compiler kann Ihren Wert bis einschließlich optimieren und niemals an einem Speicherort speichern, auf den über die Zeile hinaus zugegriffen werden kann, in der er verwendet wird (Register, Befehlskodierung usw.).
Rickster

62

Sowohl in Objective-C und Swift, die ==und !=Betreiber Test für Wertgleichheit für Zahlenwerte (zB NSInteger, NSUInteger, int, in Objective-C und Int, UIntetc. in Swift). Für Objekte (NSObject / NSNumber und Subklassen in Objective-C und Referenztypen in Swift), ==und !=Test , dass die Objekte / Referenztypen dieselben identisch sind , was - dh denselben Hashwert - oder nicht die gleichen sind identisch , was jeweils .

let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true

Swifts Identität Gleichheit Operatoren ===und !==prüfen Referenz Gleichheit - und so sollte wohl die heißen Referenz Gleichheit Betreiber IMO.

a === b // false
a === c // true

Es ist auch erwähnenswert, dass benutzerdefinierte Referenztypen in Swift (die keine Klasse unterordnen, die Equatable entspricht) nicht automatisch die Operatoren gleich gleich implementieren, die Operatoren für die Gleichheit der Identität jedoch weiterhin gelten. Auch durch die Implementierung ==, !=wird automatisch durchgeführt.

class MyClass: Equatable {
  let myProperty: String

  init(s: String) {
    myProperty = s
  }
}

func ==(lhs: MyClass, rhs: MyClass) -> Bool {
  return lhs.myProperty == rhs.myProperty
}

let myClass1 = MyClass(s: "Hello")
let myClass2 = MyClass(s: "Hello")
myClass1 == myClass2 // true
myClass1 != myClass2 // false
myClass1 === myClass2 // false
myClass1 !== myClass2 // true

Diese Gleichheitsoperatoren sind für andere Typen wie Strukturen in beiden Sprachen nicht implementiert. In Swift können jedoch benutzerdefinierte Operatoren erstellt werden, mit denen Sie beispielsweise einen Operator erstellen können, um die Gleichheit eines CGPoint zu überprüfen.

infix operator <==> { precedence 130 }
func <==> (lhs: CGPoint, rhs: CGPoint) -> Bool {
  return lhs.x == rhs.x && lhs.y == rhs.y
}

let point1 = CGPoint(x: 1.0, y: 1.0)
let point2 = CGPoint(x: 1.0, y: 1.0)
point1 <==> point2 // true

3
Entschuldigung, aber in Obj-C vergleicht der Operator == NICHT mit EQUALITY, sondern vergleicht - wie C - Zeigerreferenzen (Objektidentität).
Motti Shneor

==testet nicht auf NSNumberGleichheit in Ziel-C. NSNumberist ein NSObjectso testet es auf Identität. Der Grund, warum es manchmal funktioniert, sind markierte Zeiger / zwischengespeicherte Objektliterale. Beim Vergleich von Nicht-Literalen schlägt dies bei ausreichend großen Zahlen und auf 32-Bit-Geräten fehl.
Accatyyc

45

In Swift 3 und höher

===(oder !==)

  • Überprüft, ob die Werte identisch sind (beide zeigen auf dieselbe Speicheradresse) .
  • Im Vergleich Referenztyp .
  • Wie ==in Obj-C (Zeigergleichheit).

==(oder !=)

  • Überprüft , ob die Werte sind die gleichen .
  • Vergleicht man Werttypen .
  • Wie die Standardeinstellung isEqual:im Obj-C-Verhalten.

Hier vergleiche ich drei Instanzen (Klasse ist ein Referenztyp)

class Person {}

let person = Person()
let person2 = person
let person3 = Person()

person === person2 // true
person === person3 // false

Sie können auch isEqual:in Swift überschreiben :override func isEqual(_ object: Any?) -> Bool {}
Thomas Elliot

37

Es gibt Feinheiten bei Swifts ===, die über die bloße Zeigerarithmetik hinausgehen. In Objective-C konnten Sie zwei beliebige Zeiger (dh NSObject *) mit vergleichen== trifft dies in Swift nicht mehr zu, da Typen beim Kompilieren eine viel größere Rolle spielen.

Ein Spielplatz wird Ihnen geben

1 === 2                    // false
1 === 1                    // true
let one = 1                // 1
1 === one                  // compile error: Type 'Int' does not conform to protocol 'AnyObject'
1 === (one as AnyObject)   // true (surprisingly (to me at least))

Mit Strings müssen wir uns daran gewöhnen:

var st = "123"                                 // "123"
var ns = (st as NSString)                      // "123"
st == ns                                       // true, content equality
st === ns                                      // compile error
ns === (st as NSString)                        // false, new struct
ns === (st as AnyObject)                       // false, new struct
(st as NSString) === (st as NSString)          // false, new structs, bridging is not "free" (as in "lunch")
NSString(string:st) === NSString(string:st)    // false, new structs
var st1 = NSString(string:st)                  // "123"
var st2 = st1                                  // "123"
st1 === st2                                    // true
var st3 = (st as NSString)                     // "123"
st1 === st3                                    // false
(st as AnyObject) === (st as AnyObject)        // false

aber dann kannst du auch wie folgt spaß haben:

var st4 = st             // "123"
st4 == st                // true
st4 += "5"               // "1235"
st4 == st                // false, not quite a reference, copy on write semantics

Ich bin sicher, Sie können sich viel mehr lustige Fälle vorstellen :-)

Update für Swift 3 (wie im Kommentar von Jakub Truhlář vorgeschlagen)

1===2                                    // Compiler error: binary operator '===' cannot be applied to two 'Int' operands
(1 as AnyObject) === (2 as AnyObject)    // false
let two = 2
(2 as AnyObject) === (two as AnyObject)  // false (rather unpleasant)
(2 as AnyObject) === (2 as AnyObject)    // false (this makes it clear that there are new objects being generated)

Das sieht etwas konsistenter aus Type 'Int' does not conform to protocol 'AnyObject', aber wir bekommen es dann

type(of:(1 as AnyObject))                // _SwiftTypePreservingNSNumber.Type

Die explizite Konvertierung macht jedoch deutlich, dass möglicherweise etwas los ist. Auf der String-Seite werden die Dinge NSStringnoch verfügbar sein, solange wir import Cocoa. Dann werden wir haben

var st = "123"                                 // "123"
var ns = (st as NSString)                      // "123"
st == ns                                       // Compile error with Fixit: 'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?
st == ns as String                             // true, content equality
st === ns                                      // compile error: binary operator '===' cannot be applied to operands of type 'String' and 'NSString'
ns === (st as NSString)                        // false, new struct
ns === (st as AnyObject)                       // false, new struct
(st as NSString) === (st as NSString)          // false, new structs, bridging is not "free" (as in "lunch")
NSString(string:st) === NSString(string:st)    // false, new objects
var st1 = NSString(string:st)                  // "123"
var st2 = st1                                  // "123"
st1 === st2                                    // true
var st3 = (st as NSString)                     // "123"
st1 === st3                                    // false
(st as AnyObject) === (st as AnyObject)        // false

Es ist immer noch verwirrend, zwei String-Klassen zu haben, aber wenn Sie die implizite Konvertierung löschen, wird sie wahrscheinlich etwas greifbarer.


2
Sie können den ===Operator nicht zum Vergleichen verwenden Ints. Nicht in Swift 3.
Jakub Truhlář

Immer wenn Sie sagen, dass eine "neue Struktur" erstellt wird, wird tatsächlich ein neues Objekt (eines Klassentyps ) erstellt. ===ist für Strukturen bedeutungslos, da es sich um Werttypen handelt. Insbesondere müssen Sie drei Typen berücksichtigen: Literaltypen wie 1 oder "foo", die nicht an eine Variable gebunden sind und normalerweise nur die Kompilierung beeinflussen, da Sie sie zur Laufzeit im Allgemeinen nicht behandeln. Strukturtypen wie Intund Stringwelche erhalten Sie, wenn Sie einer Variablen ein Literal zuweisen, und Klassen wie AnyObjectund NSString.
Saagarjha

12

Wenn Sie beispielsweise zwei Instanzen einer Klasse erstellen, z myClass.

var inst1 = myClass()
var inst2 = myClass()

Sie können diese Instanzen vergleichen,

if inst1 === inst2

zitiert:

Hiermit testen Sie, ob sich zwei Objektreferenzen auf dieselbe Objektinstanz beziehen.

Auszug aus: Apple Inc. "Die schnelle Programmiersprache". iBooks. https://itun.es/sk/jEUH0.l


11

In Swift haben wir === simbol, was bedeutet, dass beide Objekte auf dieselbe Referenz und dieselbe Adresse verweisen

class SomeClass {
var a: Int;

init(_ a: Int) {
    self.a = a
}

}

var someClass1 = SomeClass(4)
var someClass2 = SomeClass(4)
someClass1 === someClass2 // false
someClass2 = someClass1
someClass1 === someClass2 // true

4

Nur ein kleiner Beitrag zum AnyObjekt.

Ich habe mit Unit-Tests gearbeitet NotificationCenter, die davon Gebrauch machenAny als Parameter verwendet werden, den ich für die Gleichheit vergleichen wollte.

Da es Anyjedoch nicht für eine Gleichstellungsoperation verwendet werden kann, musste es geändert werden. Letztendlich habe ich mich für den folgenden Ansatz entschieden, der es mir ermöglichte, in meiner spezifischen Situation Gleichheit zu erreichen, die hier anhand eines vereinfachten Beispiels gezeigt wird:

func compareTwoAny(a: Any, b: Any) -> Bool {
    return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}

Diese Funktion nutzt ObjectIdentifier , der eine eindeutige Adresse für das Objekt , mit der ich testen kann.

Ein Punkt, den Sie ObjectIdentifierpro Apple unter dem obigen Link beachten sollten :

In Swift haben nur Klasseninstanzen und Metatypen eindeutige Identitäten. Es gibt keinen Identitätsbegriff für Strukturen, Aufzählungen, Funktionen oder Tupel.


2

==wird verwendet, um zu überprüfen, ob zwei Variablen gleich sind, dh 2 == 2. ===Wenn dies jedoch für Gleichheit steht, dh wenn zwei Instanzen im Fall von Klassen auf dasselbe Objektbeispiel verweisen, wird eine Referenz erstellt, die von vielen anderen Instanzen gehalten wird.


1

Swift 4: Ein weiteres Beispiel mit Unit Tests, das nur mit === funktioniert

Hinweis: Der folgende Test schlägt mit == fehl und funktioniert mit ===

func test_inputTextFields_Delegate_is_ViewControllerUnderTest() {

        //instantiate viewControllerUnderTest from Main storyboard
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        viewControllerUnderTest = storyboard.instantiateViewController(withIdentifier: "StoryBoardIdentifier") as! ViewControllerUnderTest 
        let _ = viewControllerUnderTest.view

        XCTAssertTrue(viewControllerUnderTest.inputTextField.delegate === viewControllerUnderTest) 
    }

Und das Klassenwesen

class ViewControllerUnderTest: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var inputTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        inputTextField.delegate = self
    }
}

Der Fehler in Unit Tests, wenn Sie == verwenden, ist, Binary operator '==' cannot be applied to operands of type 'UITextFieldDelegate?' and 'ViewControllerUnderTest!'

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.