No Swift, parece haver dois operadores de igualdade: o dobro é igual a ( ==
) e o triplo é igual a ===
), qual é a diferença entre os dois?
No Swift, parece haver dois operadores de igualdade: o dobro é igual a ( ==
) e o triplo é igual a ===
), qual é a diferença entre os dois?
Respostas:
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 Equatable
protocolo e sua static func == (lhs:, rhs:) -> Bool
funçã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!
!==
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
var
ou 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.).
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
==
não testa a NSNumber
igualdade no Objective-C. NSNumber
é um NSObject
teste 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.
===
(ou !==
)==
no Obj-C (igualdade de ponteiro).==
(ou !=
)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
isEqual:
no Swift:override func isEqual(_ object: Any?) -> Bool {}
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 NSString
ainda 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.
===
operador para comparar Ints
. Not in Swift 3.
===
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 Int
e String
quais são o que você obtém quando atribui um literal a uma variável e classes como AnyObject
e NSString
.
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
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
Apenas uma pequena contribuição relacionada ao Any
objeto.
Eu estava trabalhando com testes de unidade NotificationCenter
, que faz uso deAny
como parâmetro que eu queria comparar para igualdade.
No entanto, como Any
nã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 ObjectIdentifier
por 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.
==
é 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.
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!'
==
éisEqual:
ou equivalência semântica definida por classe.===
em Swift está==
em (Obj) C - igualdade de ponteiro ou identidade de objeto.