In swift sembrano esserci due operatori di uguaglianza: il doppio uguale ( ==) e il triplo uguale ( ===), qual è la differenza tra i due?
In swift sembrano esserci due operatori di uguaglianza: il doppio uguale ( ==) e il triplo uguale ( ===), qual è la differenza tra i due?
Risposte:
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!
!==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
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.).
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
==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.
===(o !==)==in Obj-C (uguaglianza puntatore).==(o !=)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
isEqual:in Swift:override func isEqual(_ object: Any?) -> Bool {}
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.
===operatore per confrontare Ints. Non in Swift 3.
===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.
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
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
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.
==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.
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!'
==èisEqual:, o equivalenza semantica definita dalla classe.===in Swift è==in (Obj) C - uguaglianza puntatore o identità dell'oggetto.