W trybie szybkim wydaje się, że istnieją dwa operatory równości: podwójny równa się ( ==
) i potrójny równa się ( ===
), jaka jest różnica między tymi dwoma?
W trybie szybkim wydaje się, że istnieją dwa operatory równości: podwójny równa się ( ==
) i potrójny równa się ( ===
), jaka jest różnica między tymi dwoma?
Odpowiedzi:
W skrócie:
==
operator sprawdza, czy wartości ich instancji są równe, "equal to"
===
operator sprawdza, czy odniesienia wskazują tę samą instancję, "identical to"
Długa odpowiedź:
Klasy są typami referencyjnymi, wiele stałych i zmiennych może odnosić się do tego samego pojedynczego wystąpienia klasy za scenami. Odwołania do klas pozostają w stosie czasu wykonywania (RTS), a ich instancje pozostają w obszarze sterty pamięci. Kiedy kontrolujesz równość ==
, oznacza to, czy ich instancje są sobie równe. Nie musi to być ta sama instancja, aby być równym. W tym celu musisz podać kryteria równości dla swojej klasy niestandardowej. Domyślnie niestandardowe klasy i struktury nie otrzymują domyślnej implementacji operatorów równoważności, znanych jako operator „równy” ==
i operator „nie równy” !=
. Aby to zrobić, twoja klasa niestandardowa musi być zgodna z Equatable
protokołem i jego static func == (lhs:, rhs:) -> Bool
funkcją
Spójrzmy na przykład:
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.:
Ponieważ ssn (numer ubezpieczenia społecznego) jest unikalnym numerem, nie trzeba porównywać, czy ich nazwa jest równa czy nie.
let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")
if person1 == person2 {
print("the two instances are equal!")
}
Chociaż odniesienia person1 i person2 wskazują dwa różne wystąpienia w obszarze sterty, ich wystąpienia są równe, ponieważ ich liczby ssn są równe. Więc wynik będziethe two instance are equal!
if person1 === person2 {
//It does not enter here
} else {
print("the two instances are not identical!")
}
===
Kontrola operatora w przypadku odniesienia wskazują ten sam przykład, "identical to"
. Ponieważ person1 i person2 mają dwie różne instancje w obszarze sterty, nie są one identyczne i dane wyjściowethe two instance are not identical!
let person3 = person1
P.S:
Klasy są typami referencji, a referencja osoby1 jest kopiowana do osoby3 za pomocą tej operacji przypisania, dlatego oba odniesienia wskazują tę samą instancję w obszarze Sterty.
if person3 === person1 {
print("the two instances are identical!")
}
Są identyczne, a wynik będzie the two instances are identical!
!==
i ===
są operatorami tożsamości i służą do ustalenia, czy dwa obiekty mają to samo odniesienie.
Swift udostępnia również dwa operatory tożsamości (=== i! ==), których używasz do testowania, czy dwa odwołania do obiektów odnoszą się do tej samej instancji obiektu.
Fragment: Apple Inc. „Swift Programming Language”. iBooks. https://itun.es/us/jEUH0.l
var
lub let
) nazwy z wartością jest unikalną kopią - więc tworzenie wskaźników nie ma sensu, ponieważ wartość, na którą utworzyłeś wskaźnik, jest inną wartością niż ta, którą stworzyłeś po raz pierwszy. Innym jest to, że definicja semantyki wartości Swifta odciąga pamięć - kompilator może dowolnie optymalizować, aż do przechowywania wartości w miejscu dostępnym poza linią, w której jest używana (rejestr, kodowanie instrukcji itp.).
Zarówno cel C i SWIFT, ==
a !=
testy operatorów równości wartości dla wartości liczbowych (na przykład NSInteger
, NSUInteger
, int
w celu C i Int
, UInt
itp Swift). W przypadku obiektów (NSObject / NSNumber i podklasy w Objective-C i typy referencyjne w Swift) ==
i !=
przetestuj, czy obiekty / typy referencyjne są tą samą identyczną rzeczą - tj. Taką samą wartością skrótu - lub nie są odpowiednio identyczne .
let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true
Operatory równości tożsamości Swifta ===
i !==
sprawdzenie równości referencyjnej - a zatem powinny być prawdopodobnie nazywane operatorami równości referencyjnej IMO.
a === b // false
a === c // true
Warto również zauważyć, że niestandardowe typy referencyjne w Swift (które nie podklasują klasy zgodnej z Equatable) nie implementują automatycznie operatorów równości, ale operatory równości tożsamości nadal mają zastosowanie. Ponadto, poprzez wdrożenie ==
, !=
jest automatycznie realizowane.
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
Te operatory równości nie są implementowane dla innych typów, takich jak struktury w obu językach. Jednak w Swift można tworzyć niestandardowe operatory, które umożliwiłyby na przykład utworzenie operatora sprawdzającego równość CGPoint.
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
==
nie sprawdza NSNumber
równości w celu C. NSNumber
jest NSObject
więc testuje tożsamość. Powodem, dla którego SOMETIMES działa, są otagowane wskaźniki / literały obiektów buforowanych. Nie powiedzie się w przypadku wystarczająco dużych liczb i na urządzeniach 32-bitowych podczas porównywania literałów.
===
(lub !==
)==
w Obj-C (równość wskaźnika).==
(lub !=
)isEqual:
zachowanie Obj-C.Tutaj porównuję trzy instancje (klasa jest typem referencyjnym)
class Person {}
let person = Person()
let person2 = person
let person3 = Person()
person === person2 // true
person === person3 // false
isEqual:
w Swift:override func isEqual(_ object: Any?) -> Bool {}
W jerzykach są subtelności, ===
które wykraczają poza zwykłą arytmetykę wskaźników. Będąc w Objective-C mogłeś porównać dowolne dwa wskaźniki (tj. NSObject *
) Z==
tym nie jest już prawdą w Swift, ponieważ typy odgrywają znacznie większą rolę podczas kompilacji.
Plac zabaw da ci
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))
Z ciągami będziemy musieli się do tego przyzwyczaić:
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
ale możesz także bawić się w następujący sposób:
var st4 = st // "123"
st4 == st // true
st4 += "5" // "1235"
st4 == st // false, not quite a reference, copy on write semantics
Jestem pewien, że możesz wymyślić o wiele zabawniejszych przypadków :-)
Aktualizacja dla Swift 3 (zgodnie z sugestią Jakuba Truhlářa)
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)
Wygląda to nieco bardziej spójnie Type 'Int' does not conform to protocol 'AnyObject'
, ale potem się pojawia
type(of:(1 as AnyObject)) // _SwiftTypePreservingNSNumber.Type
ale wyraźna konwersja wyjaśnia, że coś może się dziać. Po stronie String rzeczy NSString
będą dostępne tak długo, jak my import Cocoa
. Wtedy będziemy mieli
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
Wciąż mylące są dwie klasy String, ale porzucenie niejawnej konwersji prawdopodobnie sprawi, że będzie ona nieco bardziej namacalna.
===
operatora do porównania Ints
. Nie w Swift 3.
===
nie ma znaczenia dla struktur, ponieważ są to typy wartości. W szczególności należy pamiętać o trzech typach: typach literalnych, takich jak 1 lub „foo”, które nie są powiązane ze zmienną i zwykle wpływają tylko na kompilację, ponieważ generalnie nie radzisz sobie z nimi podczas działania; typy struct takie jak Int
i, String
które otrzymujesz po przypisaniu literału zmiennej, oraz klasy takie jak AnyObject
i NSString
.
Na przykład, jeśli utworzysz dwa wystąpienia klasy, np . myClass
:
var inst1 = myClass()
var inst2 = myClass()
możesz porównać te wystąpienia,
if inst1 === inst2
cytowane:
którego używasz do sprawdzenia, czy oba odwołania do obiektów odnoszą się do tej samej instancji obiektu.
Fragment: Apple Inc. „Swift Programming Language”. iBooks. https://itun.es/sk/jEUH0.l
W Swift mamy simbol ===, co oznacza, że oba obiekty odnoszą się do tego samego odwołania tego samego adresu
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
Tylko niewielki wkład związany z Any
obiektem.
Pracowałem przy testach jednostkowych NotificationCenter
, które wykorzystująAny
jako parametr, który chciałem porównać dla równości.
Ponieważ jednak Any
nie można go użyć w operacji równości, konieczna była jego zmiana. Ostatecznie zdecydowałem się na następujące podejście, które pozwoliło mi uzyskać równość w mojej konkretnej sytuacji, pokazanej tutaj na uproszczonym przykładzie:
func compareTwoAny(a: Any, b: Any) -> Bool {
return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}
Ta funkcja korzysta z ObjectIdentifier , który zapewnia unikalny adres dla obiektu, umożliwiając mi testowanie.
Jedną rzecz wartą uwagi na temat ObjectIdentifier
na Apple pod powyższym linkiem:
W Swift tylko instancje klas i metatify mają unikalne tożsamości. Nie ma pojęcia tożsamości dla struktur, wyliczeń, funkcji lub krotek.
==
służy do sprawdzenia, czy dwie zmienne są równe, tj
2 == 2
. Ale w przypadku ===
równości oznacza to, że w przypadku dwóch instancji odnoszących się do tego samego obiektu przykład w przypadku klas tworzone jest odwołanie, które jest przechowywane przez wiele innych instancji.
Swift 4: Kolejny przykład użycia testów jednostkowych, który działa tylko z ===
Uwaga: test poniżej kończy się niepowodzeniem z ==, działa z ===
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)
}
A klasa jest
class ViewControllerUnderTest: UIViewController, UITextFieldDelegate {
@IBOutlet weak var inputTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
inputTextField.delegate = self
}
}
Błąd w testach jednostkowych, jeśli użyjesz ==, Binary operator '==' cannot be applied to operands of type 'UITextFieldDelegate?' and 'ViewControllerUnderTest!'
==
jestisEqual:
lub semantyczna równoważność zdefiniowana przez klasę.===
w Swift jest==
w (Obj) C - równość wskaźnika lub tożsamość obiektu.