Diferencia entre == y ===

299

En swift parece haber dos operadores de igualdad: el doble igual ( ==) y el triple igual ( ===), ¿cuál es la diferencia entre los dos?

Fela Winkelmolen
fuente

Respuestas:

149

En breve:

== el operador comprueba si sus valores de instancia son iguales, "equal to"

=== el operador comprueba si las referencias apuntan a la misma instancia, "identical to"

Respuesta larga:

Las clases son tipos de referencia, es posible que múltiples 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 memoria del montón. Cuando controlas la igualdad ==significa que sus instancias son iguales entre sí. No necesita ser la misma instancia para ser igual. Para esto, debe proporcionar un criterio de igualdad a su clase personalizada. Por defecto, 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 igual a" !=. Para hacer esto, su clase personalizada debe cumplir con el Equatableprotocolo y su static func == (lhs:, rhs:) -> Boolfunción

Veamos un 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
    }
}

P.S.: 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 de Montón, sus instancias son iguales porque sus números ssn son iguales. Entonces la salida será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 señalan la misma instancia, "identical to". Como person1 y person2 tienen dos instancias diferentes en el área Heap, no son idénticas y la salidathe two instance are not identical!

let person3 = person1

P.S: Las clases son tipos de referencia y la referencia de person1 se copia a person3 con esta operación de asignación, por lo tanto, ambas referencias apuntan a la misma instancia en el área de Heap.

if person3 === person1 {
   print("the two instances are identical!")
}

Son idénticos y la salida será the two instances are identical!

Fatih Aksu
fuente
248

!==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 puede usar 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

aglasser
fuente
49
Sip. Procedente de ObjC, ==es isEqual:, o equivalencia semántica definida por clase. ===en Swift está ==en (Obj) C: igualdad de puntero o identidad de objeto.
rickster
@rickster ¿Los valores también tienen una ubicación de memoria? Finalmente estoy en algún lugar de la memoria. ¿No puedes comparar esos? ¿O es que su ubicación de memoria no ofrece ningún valor significativo ?
Miel el
2
Hay al menos dos formas de pensar sobre cómo el lenguaje define los tipos de valor frente a la memoria. Una es que cada enlace ( varo let) de un nombre a un valor es una copia única, por lo que no tiene sentido crear punteros porque el valor al que hizo un puntero es un valor diferente al que creó por primera vez. Otra es que la definición de la semántica de valores de Swift abstrae el almacenamiento: el compilador es libre de optimizar, hasta e incluso nunca almacenar su valor en una ubicación de memoria accesible más allá de la línea donde se usa (registro, codificación de instrucciones, etc.).
rickster
62

Tanto en Objective-C y Swift, el ==y !=prueba de los operadores para la igualdad de valor para valores de número (por ejemplo, NSInteger, NSUInteger, int, en Objective-C y Int, UInt, etc. en Swift). Para objetos (NSObject / NSNumber y subclases en Objective-C y tipos de referencia en Swift), ==y !=compruebe que los objetos / tipos de referencia son la misma cosa idéntica, es decir, el mismo valor hash, o no son la misma cosa idéntica, 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 !==comprueban la igualdad referencial, y por lo tanto, probablemente deberían llamarse los operadores de igualdad referencial IMO.

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 los operadores igual a igual, 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, se pueden crear operadores personalizados 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
Scott Gardner
fuente
3
Lo sentimos, pero en Obj-C el operador == NO compara la IGUALDAD, sino que, al igual que C, compara las referencias de puntero (Identidad de objeto).
Motti Shneor
==no prueba la NSNumberigualdad en Objective-C. NSNumberes una NSObjectprueba de identidad. La razón por la que A VECES funciona es debido a punteros etiquetados / literales de objetos en caché. Fallará para números suficientemente grandes y en dispositivos de 32 bits al comparar no literales.
Accatyyc
45

En Swift 3 y superior

===(o !==)

  • Comprueba si los valores son idénticos (ambos apuntan a la misma dirección de memoria) .
  • Comparación de tipos de referencia .
  • Como ==en Obj-C (igualdad de puntero).

==(o !=)

  • Comprueba si los valores son los mismos .
  • Comparación de tipos de valores .
  • Al igual que el isEqual:comportamiento predeterminado en 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
Jakub Truhlář
fuente
También puede anular isEqual:en Swift:override func isEqual(_ object: Any?) -> Bool {}
Thomas Elliot
37

Hay sutilezas con Swifts ===que van más allá de la simple aritmética de punteros. Mientras que en Objective-C pudiste comparar dos punteros (es decir,NSObject * ) con== esto ya no es cierto en Swift ya que los tipos juegan un papel mucho más importante durante la compilación.

Un patio de juegos 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 cadenas 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 más casos divertidos :-)

Actualización para Swift 3 (como 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 consistente con Type 'Int' does not conform to protocol 'AnyObject', sin embargo, entonces obtenemos

type(of:(1 as AnyObject))                // _SwiftTypePreservingNSNumber.Type

pero la conversión explícita deja en claro que podría estar sucediendo algo. En el lado de las cadenas, las cosas NSStringseguirán estando disponibles mientras nosotros 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 eliminar la conversión implícita probablemente la haga un poco más palpable.

Patru
fuente
2
No puede usar el ===operador para comparar Ints. No en Swift 3.
Jakub Truhlář
Cada vez que dice que se está creando una "nueva estructura", lo que realmente está sucediendo es que se está creando un nuevo objeto (de un tipo de clase ). ===no tiene sentido para las estructuras ya que son tipos de valor. En particular, hay tres tipos que debe tener en cuenta: los tipos literales, como 1 o "foo", que no se han vinculado a una variable y normalmente solo afectan la compilación, ya que generalmente no se trata con ellos durante el tiempo de ejecución; tipos de estructura como Inty Stringque son lo que obtienes cuando asignas un literal a una variable, y clases como AnyObjecty NSString.
saagarjha
12

Por ejemplo, si crea dos instancias de una clase, por ejemplo myClass:

var inst1 = myClass()
var inst2 = myClass()

puedes comparar esas instancias,

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

jm666
fuente
11

En Swift tenemos === simbol, lo que significa que ambos objetos se refieren a la misma referencia, la misma dirección

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
dara
fuente
4

Solo una pequeña contribución relacionada con el Anyobjeto.

Estaba trabajando con pruebas unitarias NotificationCenter, que utilizanAny como parámetro que quería comparar para la igualdad.

Sin embargo, dado Anyque no se puede utilizar en una operación de igualdad, fue necesario cambiarlo. Finalmente, me decidí por el siguiente enfoque, que me permitió obtener la 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 ObjectIdentifierpor Apple en el enlace anterior:

En Swift, solo las instancias de clase y los metatipos tienen identidades únicas. No existe una noción de identidad para estructuras, enumeraciones, funciones o tuplas.

CodeBender
fuente
2

==se utiliza para verificar si dos variables son iguales, es decir 2 == 2. Pero en el caso de ===que signifique igualdad, es decir, si dos instancias se refieren al mismo ejemplo de objeto en el caso de clases, se crea una referencia que es mantenida por muchas otras instancias.

alisha chaudhary
fuente
1

Swift 4: otro ejemplo usando 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 las pruebas unitarias si usa == es, Binary operator '==' cannot be applied to operands of type 'UITextFieldDelegate?' and 'ViewControllerUnderTest!'

Naishta
fuente