Diferença entre == e ===

299

No Swift, parece haver dois operadores de igualdade: o dobro é igual a ( ==) e o triplo é igual a ===), qual é a diferença entre os dois?

Fela Winkelmolen
fonte

Respostas:

149

Em resumo:

== O operador verifica se seus valores de instância são iguais, "equal to"

=== O operador verifica se as referências apontam para a mesma instância, "identical to"

Resposta longa:

Classes são tipos de referência, é possível que várias constantes e variáveis ​​se refiram à mesma instância única de uma classe nos bastidores. As referências de classe permanecem no RTS (Run Time Stack) e suas instâncias permanecem na área Heap da Memória. Quando você controla a igualdade ==, significa se as instâncias são iguais entre si. Não precisa ser a mesma instância para ser igual. Para isso, você precisa fornecer um critério de igualdade para sua classe personalizada. Por padrão, as classes e estruturas personalizadas não recebem uma implementação padrão dos operadores de equivalência, conhecidos como operador "igual a" ==e "não igual a" !=. Para fazer isso, sua classe personalizada precisa estar em conformidade com o Equatableprotocolo e sua static func == (lhs:, rhs:) -> Boolfunção

Vejamos o exemplo:

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.: Como ssn (número de segurança social) é um número único, você não precisa comparar se o nome deles é igual ou não.

let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")

if person1 == person2 {
   print("the two instances are equal!")
}

Embora as referências person1 e person2 apontem duas instâncias diferentes na área Heap, suas instâncias são iguais porque seus números ssn são iguais. Então a saída seráthe two instance are equal!

if person1 === person2 {
   //It does not enter here
} else {
   print("the two instances are not identical!")
}

===O operador verifica se as referências apontam para a mesma instância "identical to",. Como person1 e person2 têm duas instâncias diferentes na área Heap, elas não são idênticas e a saídathe two instance are not identical!

let person3 = person1

P.S: Classes são tipos de referência e a referência de person1 é copiada para person3 com esta operação de atribuição, portanto, ambas as referências apontam a mesma instância na área Heap.

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

Eles são idênticos e a saída será the two instances are identical!

Fatih Aksu
fonte
248

!==e ===são operadores de identidade e são usados ​​para determinar se dois objetos têm a mesma referência.

O Swift também fornece dois operadores de identidade (=== e! ==), que você usa para testar se duas referências a objetos se referem à mesma instância de objeto.

Trecho de: Apple Inc. “A linguagem de programação Swift”. iBooks. https://itun.es/us/jEUH0.l

aglasser
fonte
49
Sim. Vindo de ObjC, ==é isEqual:ou equivalência semântica definida por classe. ===em Swift está ==em (Obj) C - igualdade de ponteiro ou identidade de objeto.
Rickster
@rickster Os valores também não têm um local de memória? Eu sou eventualmente eles estão em algum lugar na memória. Você nunca pode comparar isso? Ou é que a localização da memória deles não oferece nenhum valor significativo ?
Mel
2
Há pelo menos duas maneiras de pensar sobre como a linguagem define tipos de valor versus memória. Uma é que cada ligação ( varou let) de um nome a um valor é uma cópia exclusiva - portanto, não faz sentido criar ponteiros porque o valor para o qual você fez um ponteiro é um valor diferente daquele que você criou primeiro. Outra é que a definição de semântica de valores de Swift abstrai o armazenamento - o compilador é livre para otimizar, inclusive incluindo nunca armazenar seu valor em um local de memória acessível além da linha em que é usado (registro, codificação de instruções etc.).
Rickster
62

Em ambos Objectivo-C e Swift, a ==e !=teste de operadores para a igualdade valor para valores de número (por exemplo, NSInteger, NSUInteger, int, em Objective-C e Int, UInt, etc., em Swift). Para objectos (NSObject / NSNumber e subclasses em Objective-C e tipos de referência em Swift), ==e !=teste que os objetos / tipos de referência são a mesma coisa idêntica - ou seja, o mesmo valor de hash - ou não são a mesma coisa idêntica, respectivamente .

let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true

Os operadores de igualdade de identidade de Swift ===e !==, verificam a igualdade referencial - e, portanto, provavelmente devem ser chamados de operadores de igualdade referencial IMO.

a === b // false
a === c // true

Também vale ressaltar que os tipos de referência personalizados no Swift (que não subclassificam uma classe que é compatível com Equatable) não implementam automaticamente o igual a operadores, mas os operadores de igualdade de identidade ainda se aplicam. Além disso, implementando ==, !=é implementado automaticamente.

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

Esses operadores de igualdade não são implementados para outros tipos, como estruturas em qualquer idioma. No entanto, operadores personalizados podem ser criados no Swift, o que, por exemplo, permitiria criar um operador para verificar a igualdade de um 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
fonte
3
Desculpe, mas no Obj-C o operador == NÃO se compara à IGUALDADE, mas, como C - compara as referências de ponteiro (objeto Identity).
Motti Shneor
==não testa a NSNumberigualdade no Objective-C. NSNumberé um NSObjectteste para identidade. A razão pela qual SOMETIMES funciona é por causa de literais de ponteiros / objetos em cache marcados. Ele falhará em números grandes o suficiente e em dispositivos de 32 bits ao comparar não literais.
Accatyyc
45

No rápido 3 e acima

===(ou !==)

  • Verifica se os valores são idênticos (ambos apontam para o mesmo endereço de memória) .
  • Comparando tipos de referência .
  • Como ==no Obj-C (igualdade de ponteiro).

==(ou !=)

  • Verifica se os valores são os mesmos .
  • Comparando tipos de valor .
  • Como o padrão isEqual:no comportamento Obj-C.

Aqui eu comparo três instâncias (classe é um tipo de referência)

class Person {}

let person = Person()
let person2 = person
let person3 = Person()

person === person2 // true
person === person3 // false
Jakub Truhlář
fonte
Você também pode substituir isEqual:no Swift:override func isEqual(_ object: Any?) -> Bool {}
Thomas Elliot
37

Existem sutilezas com Swifts ===que vão além da mera aritmética de ponteiros. Enquanto em Objective-C, você foi capaz de comparar dois ponteiros (ie NSObject *) com== isso não é mais verdade no Swift, pois os tipos desempenham um papel muito maior durante a compilação.

Um Playground lhe 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))

Com as strings, teremos que nos acostumar com isso:

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

mas você também pode se divertir da seguinte maneira:

var st4 = st             // "123"
st4 == st                // true
st4 += "5"               // "1235"
st4 == st                // false, not quite a reference, copy on write semantics

Tenho certeza que você pode pensar em casos muito mais engraçados :-)

Atualização para o Swift 3 (conforme sugerido pelo comentário 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)

Isso parece um pouco mais consistente com Type 'Int' does not conform to protocol 'AnyObject', no entanto, obtemos

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

mas a conversão explícita deixa claro que pode haver algo acontecendo. No lado das strings, as coisas NSStringainda estarão disponíveis enquanto nós import Cocoa. Então teremos

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

Ainda é confuso ter duas classes String, mas descartar a conversão implícita provavelmente a tornará um pouco mais palpável.

Patru
fonte
2
Você não pode usar o ===operador para comparar Ints. Not in Swift 3.
Jakub Truhlář 10/17/17
Sempre que você diz que uma "nova estrutura" está sendo criada, o que realmente está acontecendo é que um novo objeto (de um tipo de classe ) está sendo criado. ===não faz sentido para estruturas, pois são tipos de valor. Em particular, há três tipos que você deve ter em mente: tipos literais, como 1 ou "foo", que não foram vinculados a uma variável e normalmente afetam apenas a compilação, pois geralmente você não lida com eles durante o tempo de execução; struct tipos como Inte Stringquais são o que você obtém quando atribui um literal a uma variável e classes como AnyObjecte NSString.
precisa saber é
12

Por exemplo, se você criar duas instâncias de uma classe, por exemplo myClass:

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

você pode comparar essas instâncias,

if inst1 === inst2

citado:

que você usa para testar se duas referências a objetos se referem à mesma instância de objeto.

Trecho de: Apple Inc. “A linguagem de programação Swift”. iBooks. https://itun.es/sk/jEUH0.l

jm666
fonte
11

Em Swift, temos === simbol, o que significa que ambos os objetos estão se referindo à mesma referência e mesmo endereço

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
fonte
4

Apenas uma pequena contribuição relacionada ao Anyobjeto.

Eu estava trabalhando com testes de unidade NotificationCenter, que faz uso deAny como parâmetro que eu queria comparar para igualdade.

No entanto, como Anynão pode ser usado em uma operação de igualdade, foi necessário alterá-lo. Por fim, decidi pela seguinte abordagem, que me permitiu obter igualdade em minha situação específica, mostrada aqui com um exemplo simplista:

func compareTwoAny(a: Any, b: Any) -> Bool {
    return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}

Esta função tira proveito do ObjectIdentifier , que fornece um endereço exclusivo para o objeto, permitindo que eu teste.

Um item a ser observado ObjectIdentifierpor Apple no link acima:

No Swift, apenas instâncias de classe e metatipos têm identidades exclusivas. Não há noção de identidade para estruturas, enumerações, funções ou tuplas.

CodeBender
fonte
2

==é usado para verificar se duas variáveis ​​são iguais, ie 2 == 2. Porém, no caso de ===representar igualdade, isto é, se duas instâncias referentes ao mesmo objeto exemplo, no caso de classes, for criada uma referência que é mantida por muitas outras instâncias.

alisha chaudhary
fonte
1

Swift 4: Outro exemplo usando testes de unidade que funciona apenas com ===

Nota: O teste abaixo falha com ==, funciona com ===

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) 
    }

E a classe sendo

class ViewControllerUnderTest: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var inputTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        inputTextField.delegate = self
    }
}

O erro nos testes de unidade se você usar == é, Binary operator '==' cannot be applied to operands of type 'UITextFieldDelegate?' and 'ViewControllerUnderTest!'

Naishta
fonte