==
) 和三等于 (===
),它们之间有什么区别?!==
和===
是身份运算符,用于确定两个对象是否具有相同的引用。
Swift还提供了两个身份运算符(===和!==),用于测试两个对象引用是否都指向同一个对象实例。
摘自:Apple Inc.《The Swift Programming Language》。 iBooks. https://itun.es/us/jEUH0.l
简而言之:
==
运算符检查它们实例的值是否相等,"等于"
===
运算符检查引用是否指向同一个实例,"全等于"
详细解释:
类是引用类型,多个常量和变量可以在幕后引用同一个类的单个实例。类引用保留在运行时堆栈(RTS)中,它们的实例保留在内存的堆区域中。当你使用 ==
控制相等性时,这意味着它们的实例彼此相等。不需要是相同的实例才能相等。为此,您需要向自定义类提供相等标准。默认情况下,自定义类和结构体不会收到等价操作的默认实现,称为“等于”运算符 ==
和“不等于”运算符 !=
。要实现此功能,您的自定义类需要符合 Equatable
协议并实现其 static func == (lhs:,rhs:)-> Bool
函数。
让我们看一个例子:
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.:
由于社会安全号码(ssn)是唯一的,您无需比较他们的姓名是否相同。
let person1 = Person(ssn: 5, name: "Bob")
let person2 = Person(ssn: 5, name: "Bob")
if person1 == person2 {
print("the two instances are equal!")
}
尽管person1和person2引用指向堆区的两个不同实例,但它们的实例是相等的,因为它们的ssn号码是相等的。 因此输出将是the two instance are equal!
if person1 === person2 {
//It does not enter here
} else {
print("the two instances are not identical!")
}
===
运算符检查引用是否指向相同的实例,也就是"identical to"
。由于person1和person2在堆内存中有两个不同的实例,它们不是相同的,因此输出结果为the two instance are not identical!
let person3 = person1
P.S:
类是引用类型,person1的引用被复制到person3中,因此两个引用都指向堆区中的同一实例。
if person3 === person1 {
print("the two instances are identical!")
}
它们是相同的,输出将为the two instances are identical!
在Objective-C和Swift中,==
和!=
运算符测试数字值的相等性(例如,在Objective-C中的NSInteger
, NSUInteger
, int
,以及在Swift中的Int
,UInt
等)。对于对象(Objective-C中的NSObject / NSNumber及其子类以及Swift中的引用类型),==
和!=
测试对象/引用类型是否是完全相同的东西 - 即,具有相同的哈希值 - 或者不是完全相同的东西。
let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true
Swift的恒等性运算符===
和!==
,检查引用相等性--因此,在我看来,它们应该被称为引用相等性运算符。
a === b // false
a === c // true
值得一提的是,在Swift中,自定义引用类型(不继承符合Equatable协议的类)不会自动实现等于操作符,但身份相等性运算符仍然适用。此外,通过实现==
,!=
也会被自动实现。
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
这些等式操作符对于其他类型(例如结构体)在两种语言中都没有实现。然而,在Swift中可以创建自定义操作符,例如,您可以创建一个用于检查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
==
不会测试NSNumber
的相等性。NSNumber
是一个NSObject
,因此它测试的是身份。它有时能够工作的原因是由于标记指针/缓存对象字面量。当比较非字面量和足够大的数字时,在32位设备上会失败。 - Accatyyc===
(或!==
)==
(指针相等性)==
(或!=
)isEqual:
这里我比较了三个实例 (类是引用类型)
class Person {}
let person = Person()
let person2 = person
let person3 = Person()
person === person2 // true
person === person3 // false
isEqual:
:override func isEqual(_ object: Any?) -> Bool {}
。 - Thomas ElliotSwift中使用===
存在一些微妙之处,这不仅仅是指针运算。在Objective-C中,您可以使用==
比较任何两个指针(即NSObject *
),但在Swift中不再适用,因为类型在编译期间扮演了更重要的角色。
Playground将为您提供:
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))
对于字符串,我们需要逐渐适应这个:
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
但你也可以像下面这样玩得开心:
var st4 = st // "123"
st4 == st // true
st4 += "5" // "1235"
st4 == st // false, not quite a reference, copy on write semantics
我相信你能想出更多有趣的案例 :-)
关于 Swift 3 的更新(根据 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)
这似乎更符合Type 'Int' does not conform to protocol 'AnyObject'
,但接下来我们会得到:
type(of:(1 as AnyObject)) // _SwiftTypePreservingNSNumber.Type
但是显式转换明确表明可能会发生一些事情。
在字符串方面,只要我们import Cocoa
,NSString
仍然可用。
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
仍然有两个 String 类是令人困惑的,但放弃隐式转换可能会使其稍微更容易理解一些。
===
运算符比较Ints
。 - Jakub TruhlářInt
和String
,这是当您将字面量分配给变量时得到的类型;以及类,例如AnyObject
和NSString
。 - saagarjhamyClass
:var inst1 = myClass()
var inst2 = myClass()
你可以比较这些实例,
if inst1 === inst2
引用:
你可以使用它测试两个对象引用是否都指向同一个对象实例。
节选自:Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/sk/jEUH0.l
var inst1 = myClass() var inst2 = myClass()
是两个不同的实例。 - clt60class 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
这只是与Any
对象相关的小贡献。
我正在处理与NotificationCenter
相关的单元测试,它使用Any
作为我想要比较相等性的参数。
然而,由于Any
不能用于相等操作,因此必须进行更改。最终,我采用了以下方法,使我在特定情况下获得了相等性,在这里通过简单的示例展示:
func compareTwoAny(a: Any, b: Any) -> Bool {
return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}
ObjectIdentifier
仅适用于类实例和元类型,对于结构体、枚举、函数或元组等没有身份概念。==
用于检查两个变量是否相等,即2 == 2
。但是在===
的情况下,它表示相等性,即如果两个实例引用同一个对象,则会创建一个引用,并由许多其他实例持有,例如,在类的情况下。
Swift 4: 另一个使用单元测试的示例,只能使用 === 运算符
注意:下面的测试使用 == 失败,但使用 === 成功
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)
}
而类别是
class ViewControllerUnderTest: UIViewController, UITextFieldDelegate {
@IBOutlet weak var inputTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
inputTextField.delegate = self
}
}
Binary operator '==' cannot be applied to operands of type 'UITextFieldDelegate?' and 'ViewControllerUnderTest!'
。
==
就是isEqual:
或者说是类定义的语义等价。Swift 中的===
相当于 (Obj)C 中的==
— 指针相等或对象标识。 - rickstervar
或let
)都是唯一的副本 - 因此创建指针是无意义的,因为您指向的值与您最初创建的值不同。另一种是Swift对值语义的定义抽象了存储 - 编译器可以自由地进行优化,包括从未将您的值存储在可访问的内存位置(寄存器,指令编码等)超出其使用行的范围。 - rickster