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 Equatable
protocollo 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
var
o 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
, UInt
ecc 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' NSNumber
uguaglianza in Objective-C. NSNumber
è un NSObject
modo 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 NSString
saranno 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 Int
e String
quali sono quelli che ottieni quando assegni un valore letterale a una variabile e classi come AnyObject
e 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 Any
all'oggetto.
Stavo lavorando con test unitari in giro NotificationCenter
, che fa uso diAny
come parametro che volevo confrontare per l'uguaglianza.
Tuttavia, poiché Any
non 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ò ObjectIdentifier
per 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.