Dans Swift, il semble y avoir deux opérateurs d'égalité: le double égal (==
) et le triple égal (===
), quelle est la différence entre les deux?
En bref:
==
l'opérateur vérifie si leurs valeurs d'instance sont égales,"equal to"
===
l'opérateur vérifie si les références pointent vers la même instance,"identical to"
Longue réponse:
Les classes sont des types de référence, il est possible que plusieurs constantes et variables fassent référence à la même instance d’une classe en arrière-plan. Les références de classe restent dans la pile RTS (Run Time Stack) et leurs instances restent dans la zone Heap de la mémoire. Lorsque vous contrôlez l'égalité avec==
, cela signifie que leurs instances sont égales entre elles. Il n'est pas nécessaire que la même instance soit identique. Pour cela, vous devez fournir un critère d'égalité à votre classe personnalisée. Par défaut, les classes et les structures personnalisées ne reçoivent pas d'implémentation par défaut des opérateurs d'équivalence, connue sous le nom d'opérateur "égal à"==
et d'opérateur "différent de" (!=
). Pour ce faire, votre classe personnalisée doit se conformer àEquatable
protocole et c'eststatic func == (lhs:, rhs:) -> Bool
function
Regardons l'exemple:
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.:
Puisque ssn (numéro de sécurité sociale) est un numéro unique, vous n'avez pas besoin de comparer si leur nom est égal ou différent.
let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")
if person1 == person2 {
print("the two instances are equal!")
}
Bien que les références personne1 et personne2 pointent vers deux instances différentes dans la zone de segment de mémoire, leurs instances sont égales car leurs nombres ssn sont égaux. Donc, le résultat sera the two instance are equal!
if person1 === person2 {
//It does not enter here
} else {
print("the two instances are not identical!")
}
===
operator vérifie si les références pointent vers la même instance,"identical to"
. Puisque personne1 et personne2 ont deux instances différentes dans la zone de tas, elles ne sont pas identiques et la sortie the two instance are not identical!
let person3 = person1
P.S:
Les classes sont des types de référence et la référence de person1 est copiée dans person3 avec cette opération d'affectation. Par conséquent, les deux références désignent la même instance dans la zone Heap.
if person3 === person1 {
print("the two instances are identical!")
}
Ils sont identiques et le résultat sera the two instances are identical!
!==
et ===
sont des opérateurs d'identité et permettent de déterminer si deux objets ont la même référence.
Swift fournit également deux opérateurs d'identité (=== et! ==), que vous utilisez pour vérifier si deux références d'objet se rapportent toutes deux à la même instance d'objet.
Extrait de: Apple Inc. «Le langage de programmation Swift.» IBooks. https://itun.es/us/jEUH0.l
Dans Objective-C et Swift, les opérateurs ==
et !=
testent l'égalité des valeurs pour les valeurs numériques (par exemple, NSInteger
, NSUInteger
, int
, en Objective-C et Int
, UInt
, etc. dans Swift). Pour les objets (NSObject/NSNumber et les sous-classes dans Objective-C et les types de référence dans Swift), ==
et !=
vérifient que les objets/types de référence sont identiques (c'est-à-dire, même valeur de hachage) ou identiques. , respectivement.
let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true
Les opérateurs identité d'égalité de Swift, ===
et !==
, vérifient l'égalité référentielle - et devraient donc probablement s'appeler les opérateurs égalité référentielle, OMI.
a === b // false
a === c // true
Il convient également de noter que les types de référence personnalisés dans Swift (qui ne sous-classent pas une classe conforme à Equatable) n'implémentent pas automatiquement les opérateurs égal à, mais les opérateurs identity equal s'appliquent toujours. De plus, en implémentant ==
, !=
est automatiquement implémenté.
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
Ces opérateurs d'égalité ne sont pas implémentés pour d'autres types tels que des structures dans l'une ou l'autre langue. Toutefois, des opérateurs personnalisés peuvent être créés dans Swift, ce qui vous permettrait, par exemple, de créer un opérateur pour vérifier l’égalité d’un 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
===
(ou !==
)==
dans Obj-C (égalité de pointeur).==
(ou !=
)isEqual:
par défaut dans le comportement Obj-C.Ici, je compare trois instances (classe est un type de référence)
class Person {}
let person = Person()
let person2 = person
let person3 = Person()
person === person2 // true
person === person3 // false
Il y a des subtilités dans Swifts ===
qui vont au-delà du simple calcul arithmétique. En Objective-C, vous avez été en mesure de comparer deux pointeurs (à savoir NSObject *
) avec ==
, ce n’est plus le cas dans Swift car les types jouent un rôle beaucoup plus important pendant la compilation.
Un terrain de jeu vous donnera
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))
Avec des cordes, nous devrons nous habituer à ceci:
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
mais alors vous pouvez aussi vous amuser comme suit:
var st4 = st // "123"
st4 == st // true
st4 += "5" // "1235"
st4 == st // false, not quite a reference, copy on write semantics
Je suis sûr que vous pouvez penser à des cas beaucoup plus drôles :-)
Mise à jour pour Swift 3 (comme suggéré par le commentaire de Jakub Truhlář)
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)
Cela semble un peu plus compatible avec Type 'Int' does not conform to protocol 'AnyObject'
, cependant nous obtenons alors
type(of:(1 as AnyObject)) // _SwiftTypePreservingNSNumber.Type
mais la conversion explicite indique clairement qu'il se peut qu'il se passe quelque chose . Du côté chaîne de choses, NSString
sera toujours disponible tant que nous aurons import Cocoa
. Ensuite, nous aurons
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
Il est toujours déroutant d’avoir deux classes String, mais abandonner la conversion implicite en fera probablement un little plus palpable.
Par exemple, si vous créez deux instances d’une classe, par exemple, myClass
:
var inst1 = myClass()
var inst2 = myClass()
vous pouvez comparer ces cas,
if inst1 === inst2
cité:
que vous utilisez pour vérifier si deux références d’objet renvoient toutes deux au même instance d'objet.
Extrait de: Apple Inc. «Le langage de programmation Swift.» IBooks. https://itun.es/sk/jEUH0.l
Dans Swift, nous avons === simbol, ce qui signifie que les deux objets font référence à la même adresse de référence, même adresse
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
Juste une contribution mineure liée à l'objet Any
.
Je travaillais avec des tests unitaires autour de NotificationCenter
, qui utilise Any
comme paramètre que je voulais comparer pour l’égalité.
Cependant, comme Any
ne peut pas être utilisé dans une opération d'égalité, il était nécessaire de la changer. En fin de compte, j'ai opté pour l'approche suivante, qui m'a permis d'obtenir l'égalité dans ma situation spécifique, illustrée ici par un exemple simpliste:
func compareTwoAny(a: Any, b: Any) -> Bool {
return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}
Cette fonction tire parti de ObjectIdentifier , qui fournit une adresse unique à l’objet, me permettant de tester.
Un élément à noter cependant à propos de ObjectIdentifier
par Apple sur le lien ci-dessus:
Dans Swift, seuls les instances de classe et les métatypes ont une identité unique . Il n'y a pas de notion d'identité pour les structures, les enums, les fonctions ou tuples.
==
est utilisé pour vérifier si deux variables sont égales, c'est-à-dire 2 == 2
. Mais dans le cas de ===
, cela signifie une égalité, c'est-à-dire si deux instances faisant référence au même objet, par exemple dans le cas de classes, une référence est créée, qui est détenue par de nombreuses autres instances.
Swift 4: un autre exemple utilisant Unit Tests qui ne fonctionne qu'avec ===
Remarque: le test ci-dessous échoue avec ==, fonctionne avec ===
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)
}
Et la classe étant
class ViewControllerUnderTest: UIViewController, UITextFieldDelegate {
@IBOutlet weak var inputTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
inputTextField.delegate = self
}
}
L'erreur dans les tests unitaires si vous utilisez == est, Binary operator '==' cannot be applied to operands of type 'UITextFieldDelegate?' and 'ViewControllerUnderTest!'
Égalité des valeurs
isEqual:
==
Égalité de référence
==
===
Lire la suite ici