In Swift scheint es zwei Gleichheitsoperatoren zu geben: die doppelten Gleichen ( ==
) und die dreifachen Gleichen ( ===
). Was ist der Unterschied zwischen den beiden?
In Swift scheint es zwei Gleichheitsoperatoren zu geben: die doppelten Gleichen ( ==
) und die dreifachen Gleichen ( ===
). Was ist der Unterschied zwischen den beiden?
Antworten:
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 Equatable
Protokoll und seiner static func == (lhs:, rhs:) -> Bool
Funktion 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!
!==
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
var
oder 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.).
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
, UInt
etc. 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
==
testet nicht auf NSNumber
Gleichheit in Ziel-C. NSNumber
ist ein NSObject
so 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.
===
(oder !==
)==
in Obj-C (Zeigergleichheit).==
(oder !=
)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
isEqual:
in Swift überschreiben :override func isEqual(_ object: Any?) -> Bool {}
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 NSString
noch 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.
===
Operator nicht zum Vergleichen verwenden Ints
. Nicht in Swift 3.
===
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 Int
und String
welche erhalten Sie, wenn Sie einer Variablen ein Literal zuweisen, und Klassen wie AnyObject
und NSString
.
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
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
Nur ein kleiner Beitrag zum Any
Objekt.
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 Any
jedoch 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 ObjectIdentifier
pro 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.
==
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.
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!'
==
istisEqual:
oder klassendefinierte semantische Äquivalenz.===
in Swift ist==
in (Obj) C - Zeigergleichheit oder Objektidentität.