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?
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?
Réponses:
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 se réfèrent à la même instance unique d'une classe dans les coulisses. Les références de classe restent dans Run Time Stack (RTS) 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 les unes aux autres. Il n'est pas nécessaire que la même instance soit identique. Pour cela, vous devez fournir des critères d'égalité à votre classe personnalisée. Par défaut, les classes et structures personnalisées ne reçoivent pas d'implémentation par défaut des opérateurs d'équivalence, appelés opérateurs «égal à» ==
et «non égal à» !=
. Pour ce faire, votre classe personnalisée doit respecter le Equatable
protocole et sa static func == (lhs:, rhs:) -> Bool
fonction
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.:
Étant donné que 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 non.
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 person1 et person2 pointent deux instances différentes dans la zone Heap, leurs instances sont égales car leurs numéros SSN sont égaux. Ainsi, la sortie serathe two instance are equal!
if person1 === person2 {
//It does not enter here
} else {
print("the two instances are not identical!")
}
===
opérateur vérifie si les références pointent la même instance, "identical to"
. Étant donné que personne1 et personne2 ont deux instances différentes dans la zone Heap, elles ne sont pas identiques et la sortiethe 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, les deux références pointent donc la même instance dans la zone Heap.
if person3 === person1 {
print("the two instances are identical!")
}
Ils sont identiques et la sortie sera the two instances are identical!
!==
et ===
sont des opérateurs d'identité et sont utilisés pour 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 tester si deux références d'objet font toutes deux référence à la même instance d'objet.
Extrait de: Apple Inc. «The Swift Programming Language». iBooks. https://itun.es/us/jEUH0.l
var
ou let
) d'un nom à une valeur est une copie unique - il est donc inutile de créer des pointeurs car la valeur à laquelle vous avez fait un pointeur est une valeur différente de celle que vous avez créée en premier. Un autre est que la définition de la sémantique des valeurs par Swift supprime le stockage - le compilateur est libre d'optimiser, jusqu'à et y compris ne jamais stocker votre valeur à un emplacement de mémoire accessible au-delà de la ligne où elle est utilisée (registre, encodage d'instructions, etc.).
Dans les deux Objective-C et Swift, les ==
et !=
opérateurs test pour l' égalité de valeur pour les valeurs numériques (par exemple NSInteger
, NSUInteger
, int
, en Objective-C et Int
, UInt
, etc. Swift). Pour les objets (NSObject / NSNumber et les sous-classes dans Objective-C et les types de référence dans Swift), ==
et !=
tester que les objets / types de référence sont la même chose identique - c'est-à-dire la même valeur de hachage - ou ne sont pas la même chose identique, respectivement .
let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true
Les opérateurs d' égalité d'identité de Swift , ===
et !==
vérifier l'égalité référentielle - et donc, devraient probablement être appelés les opérateurs d' égalité référentielle IMO.
a === b // false
a === c // true
Il convient également de souligner 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 égaux à, mais les opérateurs d' égalité d'identité s'appliquent toujours. En outre, en implémentant ==
, !=
est implémenté automatiquement.
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 les structures dans les deux langues. Cependant, 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
==
ne teste pas l' NSNumber
égalité dans Objective-C. NSNumber
est NSObject
donc un test d'identité. La raison pour laquelle cela fonctionne PARFOIS est à cause des pointeurs marqués / des littéraux d'objets mis en cache. Il échouera pour des nombres suffisamment grands et sur des appareils 32 bits lors de la comparaison de non-littéraux.
===
(ou !==
)==
dans Obj-C (égalité de pointeur).==
(ou !=
)isEqual:
par défaut dans le comportement Obj-C.Ici, je compare trois instances (la 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
isEqual:
dans Swift:override func isEqual(_ object: Any?) -> Bool {}
Il y a des subtilités avec les martinets ===
qui vont au-delà de la simple arithmétique des pointeurs. Dans Objective-C, vous avez pu comparer deux pointeurs (c.-à-d. NSObject *
) Avec==
cela n'est plus vrai 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 les chaînes, 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 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 cohérent avec Type 'Int' does not conform to protocol 'AnyObject'
, mais nous obtenons ensuite
type(of:(1 as AnyObject)) // _SwiftTypePreservingNSNumber.Type
mais la conversion explicite montre clairement qu'il pourrait y avoir quelque chose. Du côté des chaînes, les choses NSString
seront toujours disponibles aussi longtemps que nous 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 la rendra probablement un peu plus palpable.
===
opérateur pour comparer Ints
. Pas dans Swift 3.
===
n'a pas de sens pour les structures car ce sont des types de valeur. En particulier, vous devez garder à l'esprit trois types: les types littéraux, tels que 1 ou "foo", qui ne sont pas liés à une variable et n'affectent normalement que la compilation, car vous ne les traitez généralement pas pendant l'exécution; types de struct tels que Int
et String
qui sont ce que vous obtenez lorsque vous affectez un littéral à une variable, et des classes telles que AnyObject
et NSString
.
Par exemple, si vous créez deux instances d'une classe, par exemple myClass
:
var inst1 = myClass()
var inst2 = myClass()
vous pouvez comparer ces instances,
if inst1 === inst2
cité:
que vous utilisez pour tester si deux références d'objet font référence à la même instance d'objet.
Extrait de: Apple Inc. «The Swift Programming Language». 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 référence et à la 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 petite contribution liée à l' Any
objet.
Je travaillais avec des tests unitaires autour NotificationCenter
, qui utilise Any
comme paramètre que je voulais comparer pour l'égalité.
Cependant, Any
ne pouvant pas être utilisé dans une opération d'égalité, il a fallu le modifier. En fin de compte, j'ai opté pour l'approche suivante, qui m'a permis d'obtenir l'égalité dans ma situation spécifique, présentée ici avec un exemple simpliste:
func compareTwoAny(a: Any, b: Any) -> Bool {
return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}
Cette fonction tire parti d' ObjectIdentifier , qui fournit une adresse unique pour l'objet, me permettant de tester.
Un élément à noter cependant ObjectIdentifier
par Apple sur le lien ci-dessus:
Dans Swift, seules les instances de classe et les métatypes ont des identités uniques. Il n'y a aucune notion d'identité pour les structures, les énumérations, les fonctions ou les tuples.
==
est utilisé pour vérifier si deux variables sont égales ie
2 == 2
. Mais dans le cas où ===
cela signifie égalité, c'est-à-dire que si deux instances se référant au même exemple d'objet dans le cas des classes, une référence est créée qui est détenue par de nombreuses autres instances.
Swift 4: Un autre exemple utilisant des tests unitaires 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!'
==
isisEqual:
, ou l'équivalence sémantique définie par la classe.===
dans Swift est==
dans (Obj) C - égalité de pointeur, ou identité d'objet.