swift - the - Diferencia entre== y===
swift(lenguaje de programación) (10)
En Swift parece haber dos operadores de igualdad: el doble es igual a ( ==
) y el triple es igual a ( ===
), ¿cuál es la diferencia entre los dos?
En Swift 3 y arriba
===
(o !==
)
- Comprueba si los valores son idénticos (ambos apuntan a la misma dirección de memoria) .
- Comparando tipos de referencia .
- Como
==
en Obj-C (igualdad de puntero).
==
(o !=
)
- Comprueba si los valores son los mismos .
- Comparando tipos de valores .
- Al igual que el predeterminado
isEqual:
en el comportamiento de Obj-C.
Aquí comparo tres instancias (la clase es un tipo de referencia)
class Person {}
let person = Person()
let person2 = person
let person3 = Person()
person === person2 // true
person === person3 // false
En Swift tenemos === símbolo, lo que significa que ambos objetos se refieren a la misma dirección de referencia
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
Hay sutilezas con Swifts ===
que van más allá de la mera aritmética de punteros. Mientras que en Objective-C pudo comparar dos punteros (es decir, NSObject *
) con ==
esto ya no es cierto en Swift, ya que los tipos juegan un papel mucho mayor durante la compilación.
Un patio de recreo te dará
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))
Con cuerdas tendremos que acostumbrarnos a esto:
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
Pero también puedes divertirte de la siguiente manera:
var st4 = st // "123"
st4 == st // true
st4 += "5" // "1235"
st4 == st // false, not quite a reference, copy on write semantics
Estoy seguro de que puedes pensar en muchos casos más divertidos :-)
Actualización para Swift 3 (como lo sugiere el comentario 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)
Esto parece un poco más coherente con el Type ''Int'' does not conform to protocol ''AnyObject''
, pero luego obtenemos
type(of:(1 as AnyObject)) // _SwiftTypePreservingNSNumber.Type
pero la conversión explícita deja claro que puede haber algo en marcha. En el lado de la cadena de cosas, NSString
seguirá estando disponible siempre que NSString
import Cocoa
. Entonces tendremos
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
Todavía es confuso tener dos clases de String, pero si se elimina la conversión implícita, probablemente sea más palpable.
Por ejemplo, si creas dos instancias de una clase, por ejemplo, myClass
:
var inst1 = myClass()
var inst2 = myClass()
puedes comparar esos casos,
if inst1 === inst2
citado
que utiliza para probar si dos referencias de objeto se refieren a la misma instancia de objeto.
Extracto de: Apple Inc. "El lenguaje de programación Swift". IBooks. https://itun.es/sk/jEUH0.l
Solo una pequeña contribución relacionada con el objeto Any
.
Trabajaba con pruebas unitarias en torno a NotificationCenter
, que utiliza Any
como un parámetro que quería comparar para la igualdad.
Sin embargo, dado que Any
no se puede utilizar en una operación de igualdad, fue necesario cambiarlo. En última instancia, me decidí por el siguiente enfoque, que me permitió obtener igualdad en mi situación específica, que se muestra aquí con un ejemplo simplista:
func compareTwoAny(a: Any, b: Any) -> Bool {
return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}
Esta función aprovecha ObjectIdentifier , que proporciona una dirección única para el objeto, lo que me permite probar.
Un elemento a tener en cuenta sobre ObjectIdentifier
por Apple en el enlace anterior:
En Swift, solo las instancias de clase y los metatipos tienen identidades únicas. No hay noción de identidad para estructuras, enumeraciones, funciones o tuplas.
Tanto en Objective-C como en Swift, los operadores ==
y !=
NSUInteger
igualdad de valores para valores numéricos (por ejemplo, NSInteger
, NSUInteger
, int
, en Objective-C e Int
, UInt
, etc. en Swift). Para objetos (NSObject / NSNumber y subclases en Objective-C y tipos de referencia en Swift), ==
y !=
Comprueban que los objetos / tipos de referencia son lo mismo - es decir, el mismo valor hash - o no son lo mismo Lo idéntico, respectivamente.
let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true
Los operadores de igualdad de identidad de Swift, ===
y !==
, verifican la igualdad referencial, y por lo tanto, probablemente deberían llamarse los operadores de igualdad referencial de la OMI.
a === b // false
a === c // true
También vale la pena señalar que los tipos de referencia personalizados en Swift (que no subclasifican una clase que se ajusta a Equatable) no implementan automáticamente el igual a los operadores, pero los operadores de igualdad de identidad aún se aplican. Además, al implementar ==
!=
Se implementa automáticamente.
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
Estos operadores de igualdad no se implementan para otros tipos, como estructuras en cualquier idioma. Sin embargo, los operadores personalizados se pueden crear en Swift, lo que, por ejemplo, le permitiría crear un operador para verificar la igualdad de 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
!==
y ===
son operadores de identidad y se utilizan para determinar si dos objetos tienen la misma referencia.
Swift también proporciona dos operadores de identidad (=== y! ==), que utiliza para probar si dos referencias de objeto se refieren a la misma instancia de objeto.
Extracto de: Apple Inc. "El lenguaje de programación Swift". IBooks. https://itun.es/us/jEUH0.l
==
se utiliza para verificar si dos variables son iguales, es decir, 2 == 2
. Pero en el caso de ===
significa igualdad, es decir, si se crean dos instancias que se refieren al mismo ejemplo de objeto en el caso de las clases, se crea una referencia que es mantenida por muchas otras instancias.
En breve:
==
operador comprueba si sus valores de instancia son iguales, "equal to"
===
operador ===
comprueba si las referencias apuntan a la misma instancia, "identical to"
Respuesta larga:
Las clases son tipos de referencia, es posible que varias constantes y variables se refieran a la misma instancia única de una clase detrás de escena. Las referencias de clase permanecen en la pila de tiempo de ejecución (RTS) y sus instancias permanecen en el área de almacenamiento dinámico de la memoria. Cuando controlas la igualdad con ==
significa que sus instancias son iguales entre sí. No necesita ser la misma instancia para ser igual. Para ello, debe proporcionar un criterio de igualdad a su clase personalizada. De forma predeterminada, las clases y estructuras personalizadas no reciben una implementación predeterminada de los operadores de equivalencia, conocidos como el operador "igual a" ==
y el operador "no es igual a" !=
. Para hacer esto, su clase personalizada debe cumplir Equatable
protocolo Equatable
y su static func == (lhs:, rhs:) -> Bool
Función static func == (lhs:, rhs:) -> Bool
Veamos el ejemplo:
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
}
}
PS:
Dado que ssn (número de seguro social) es un número único, no necesita comparar si su nombre es igual o no.
let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")
if person1 == person2 {
print("the two instances are equal!")
}
Aunque las referencias de persona1 y persona2 señalan dos instancias diferentes en el área del montón, sus instancias son iguales porque sus números ssn son iguales. ¡Así que la salida serán the two instance are equal!
if person1 === person2 {
//It does not enter here
} else {
print("the two instances are not identical!")
}
===
operador ===
comprueba si las referencias apuntan a la misma instancia, "identical to"
. Como person1 y person2 tienen dos instancias diferentes en el área de Heap, no son idénticas y la salida de the two instance are not identical!
let person3 = person1
PS:
Las clases son tipos de referencia y la referencia de person1 se copia a person3 con esta operación de asignación, por lo que ambas referencias apuntan a la misma instancia en el área Heap.
if person3 === person1 {
print("the two instances are identical!")
}
¡Son idénticos y la salida serán the two instances are identical!
Swift 4: otro ejemplo que usa Pruebas Unitarias que solo funciona con ===
Nota: la prueba a continuación falla con ==, funciona con ===
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)
}
Y la clase siendo
class ViewControllerUnderTest: UIViewController, UITextFieldDelegate {
@IBOutlet weak var inputTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
inputTextField.delegate = self
}
}
El error en Pruebas unitarias si usa == es, el Binary operator ''=='' cannot be applied to operands of type ''UITextFieldDelegate?'' and ''ViewControllerUnderTest!''
Binary operator ''=='' cannot be applied to operands of type ''UITextFieldDelegate?'' and ''ViewControllerUnderTest!''