Differenza tra == e ===


299

In swift sembrano esserci due operatori di uguaglianza: il doppio uguale ( ==) e il triplo uguale ( ===), qual è la differenza tra i due?

Risposte:


149

In breve:

== l'operatore verifica se i loro valori di istanza sono uguali, "equal to"

=== l'operatore verifica se i riferimenti indicano la stessa istanza, "identical to"

Risposta lunga:

Le classi sono tipi di riferimento, è possibile che più costanti e variabili facciano riferimento alla stessa singola istanza di una classe dietro le quinte. I riferimenti alle classi rimangono nello Run Time Stack (RTS) e le loro istanze rimangono nell'area Heap della memoria. Quando controlli l'uguaglianza con ==essa significa se le loro istanze sono uguali tra loro. Non deve essere la stessa istanza per essere uguale. Per questo è necessario fornire un criterio di uguaglianza alla propria classe personalizzata. Per impostazione predefinita, le classi e le strutture personalizzate non ricevono un'implementazione predefinita degli operatori di equivalenza, noti come operatore "uguale a" ==e "non uguale a" !=. Per fare ciò la tua classe personalizzata deve conformarsi al Equatableprotocollo ed èstatic func == (lhs:, rhs:) -> Bool funzione

Diamo un'occhiata all'esempio:

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.: Poiché ssn (numero di previdenza sociale) è un numero univoco, non è necessario confrontare se il loro nome è uguale o meno.

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

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

Sebbene i riferimenti person1 e person2 indicano due diverse istanze nell'area Heap, le loro istanze sono uguali perché i loro numeri ssn sono uguali. Quindi l'output saràthe two instance are equal!

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

===operatore controlla se i riferimenti puntano alla stessa istanza, "identical to". Poiché person1 e person2 hanno due diverse istanze nell'area Heap, non sono identici e l'outputthe two instance are not identical!

let person3 = person1

P.S: Le classi sono tipi di riferimento e il riferimento di person1 viene copiato in person3 con questa operazione di assegnazione, quindi entrambi i riferimenti indicano la stessa istanza nell'area Heap.

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

Sono identici e l'uscita sarà the two instances are identical!


248

!==e ===sono operatori di identità e vengono utilizzati per determinare se due oggetti hanno lo stesso riferimento.

Swift fornisce anche due operatori identità (=== e! ==), che viene utilizzato per verificare se due riferimenti a oggetti si riferiscono entrambi alla stessa istanza di oggetto.

Estratto da: Apple Inc. "The Swift Programming Language". iBook. https://itun.es/us/jEUH0.l


49
Sì. Proveniente da ObjC, ==è isEqual:, o equivalenza semantica definita dalla classe. ===in Swift è ==in (Obj) C - uguaglianza puntatore o identità dell'oggetto.
rickster,

I valori di @rickster Non hanno anche una posizione di memoria? Alla fine sono da qualche parte nella memoria. Non puoi mai confrontare quelli? O è che la loro posizione di memoria non offre alcun valore significativo ?
Miele

2
Esistono almeno due modi per pensare a come la lingua definisce i tipi di valore rispetto alla memoria. Uno è che ogni legame ( varo let) di un nome a un valore è una copia unica, quindi non ha senso creare puntatori perché il valore a cui hai fatto un puntatore è un valore diverso da quello che hai creato per la prima volta. Un altro è che la definizione di semantica di valore di Swift estrae l'archiviazione: il compilatore è libero di ottimizzare, fino a includere la memorizzazione del valore in una posizione di memoria accessibile oltre la linea in cui viene utilizzato (registro, codifica delle istruzioni, ecc.).
rickster

62

In entrambi Objective-C e Swift, la ==e !=test di operatori per l'uguaglianza per i valori numerici (ad esempio, NSInteger, NSUInteger, int, in Objective-C e Int, UIntecc in Swift). Per gli oggetti (NSObject / NSNumber e sottoclassi in Objective-C e tipi di riferimento in Swift), ==e !=verifica che gli oggetti / tipi di riferimento siano la stessa cosa identica - ovvero lo stesso valore di hash - o che non siano la stessa cosa identica, rispettivamente .

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

Gli operatori di uguaglianza di identità di Swift , ===e !==, controllano l'uguaglianza referenziale - e quindi, dovrebbero probabilmente essere chiamati operatori di uguaglianza referenziale IMO.

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

Vale anche la pena sottolineare che i tipi di riferimento personalizzati in Swift (che non includono una sottoclasse di una classe conforme a Equatable) non implementano automaticamente l'eguale per gli operatori, ma gli operatori di uguaglianza di identità continuano ad applicarsi. Inoltre, implementando ==, !=viene implementato 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

Questi operatori di uguaglianza non sono implementati per altri tipi come strutture in entrambe le lingue. Tuttavia, in Swift è possibile creare operatori personalizzati che, ad esempio, consentono di creare un operatore per verificare l'uguaglianza di 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

3
Siamo spiacenti, ma in Obj-C l'operatore == NON confronta per EQUALITY, ma piuttosto - come C - confronta i riferimenti del puntatore (oggetto Identity).
Motti Shneor,

==non verifica l' NSNumberuguaglianza in Objective-C. NSNumberè un NSObjectmodo per testare l'identità. Il motivo per cui funziona SOMETIMES è a causa di puntatori con tag / valori letterali di oggetti memorizzati nella cache. Fallirà per numeri abbastanza grandi e su dispositivi a 32 bit quando si confrontano i non-letterali.
Accatyyc,

45

In rapido 3 e sopra

===(o !==)

  • Verifica se i valori sono identici (entrambi puntano allo stesso indirizzo di memoria) .
  • Confronto tra tipi di riferimento .
  • Come ==in Obj-C (uguaglianza puntatore).

==(o !=)

  • Verifica se i valori sono uguali .
  • Confronto tra tipi di valore .
  • Come l'impostazione predefinita isEqual:nel comportamento Obj-C.

Qui confronto tre istanze (la classe è un tipo di riferimento)

class Person {}

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

person === person2 // true
person === person3 // false

Puoi anche eseguire l'override isEqual:in Swift:override func isEqual(_ object: Any?) -> Bool {}
Thomas Elliot il

37

Ci sono sottigliezze con Swift ===che vanno oltre l'aritmetica del semplice puntatore. Mentre in Objective-C sei riuscito a confrontare due puntatori (cioè NSObject *) con== questo non è più vero in Swift poiché i tipi svolgono un ruolo molto più importante durante la compilazione.

Un parco giochi ti 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 le stringhe dovremo abituarci a questo:

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

ma poi puoi anche divertirti come segue:

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

Sono sicuro che puoi pensare a casi molto più divertenti :-)

Aggiornamento per Swift 3 (come suggerito dal commento di 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)

Questo sembra un po 'più coerente con Type 'Int' does not conform to protocol 'AnyObject', tuttavia poi otteniamo

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

ma la conversione esplicita chiarisce che potrebbe esserci qualcosa in corso. Sul lato String delle cose NSStringsaranno ancora disponibili fino a quando noi import Cocoa. Quindi avremo

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

È ancora confuso avere due classi String, ma abbandonare la conversione implicita probabilmente la renderà un po ' più palpabile.


2
Non è possibile utilizzare l' ===operatore per confrontare Ints. Non in Swift 3.
Jakub Truhlář

Ogni volta che dici che viene creata una "nuova struttura", ciò che sta realmente accadendo è la creazione di un nuovo oggetto (di un tipo di classe ). ===non ha senso per le strutture poiché sono tipi di valore. In particolare, ci sono tre tipi che devi tenere a mente: tipi letterali, come 1 o "pippo", che non sono stati associati a una variabile e normalmente influenzano solo la compilazione poiché generalmente non li gestisci durante il runtime; tipi di struttura come Inte Stringquali sono quelli che ottieni quando assegni un valore letterale a una variabile e classi come AnyObjecte NSString.
Saagarjha,

12

Ad esempio, se si creano due istanze di una classe, ad esempio myClass:

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

puoi confrontare quelle istanze,

if inst1 === inst2

citato:

che si utilizza per verificare se due riferimenti a oggetti si riferiscono entrambi alla stessa istanza di oggetto.

Estratto da: Apple Inc. "The Swift Programming Language". iBook. https://itun.es/sk/jEUH0.l


11

In Swift abbiamo === simbol che significa che entrambi gli oggetti si riferiscono allo stesso indirizzo di riferimento stesso

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

4

Solo un piccolo contributo relativo Anyall'oggetto.

Stavo lavorando con test unitari in giro NotificationCenter, che fa uso diAny come parametro che volevo confrontare per l'uguaglianza.

Tuttavia, poiché Anynon può essere utilizzato in un'operazione di uguaglianza, è stato necessario modificarlo. Alla fine, ho optato per il seguente approccio, che mi ha permesso di ottenere l'uguaglianza nella mia situazione specifica, mostrato qui con un esempio semplicistico:

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

Questa funzione sfrutta ObjectIdentifier , che fornisce un indirizzo univoco per l'oggetto, permettendomi di testare.

Un elemento da notare però ObjectIdentifierper Apple al link sopra:

In Swift, solo le istanze di classe e i metatipi hanno identità univoche. Non esiste una nozione di identità per strutture, enum, funzioni o tuple.


2

==viene utilizzato per verificare se due variabili sono uguali, ad es 2 == 2. Ma nel caso in cui ===sia sinonimo di uguaglianza, cioè se due istanze si riferiscono allo stesso oggetto oggetto in caso di classi, viene creato un riferimento che è tenuto da molte altre istanze.


1

Swift 4: Un altro esempio che utilizza Test unit che funziona solo con ===

Nota: il test seguente non riesce con ==, funziona 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) 
    }

E l'essere di classe

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

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

L'errore nei Test unitari se usi == è, Binary operator '==' cannot be applied to operands of type 'UITextFieldDelegate?' and 'ViewControllerUnderTest!'

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.