ความแตกต่างระหว่าง == และ ===


300

อย่างรวดเร็วดูเหมือนว่าจะมีผู้ประกอบการสองรายที่เท่าเทียมกัน: double equals ( ==) และ triple เท่ากับ ( ===) อะไรคือความแตกต่างระหว่างทั้งสอง?

คำตอบ:


149

ในระยะสั้น:

== ผู้ประกอบการตรวจสอบว่าค่าอินสแตนซ์ของพวกเขาจะเท่ากัน "equal to"

=== ผู้ประกอบการตรวจสอบว่าการอ้างอิงชี้ไปที่อินสแตนซ์เดียวกัน "identical to"

คำตอบยาว:

คลาสเป็นชนิดของการอ้างอิงมันเป็นไปได้ที่ค่าคงที่และตัวแปรหลายตัวสามารถอ้างถึงอินสแตนซ์เดียวของชั้นเรียนที่อยู่เบื้องหลัง การอ้างอิงคลาสอยู่ใน Run Time Stack (RTS) และอินสแตนซ์ของมันยังคงอยู่ในพื้นที่ Heap ของหน่วยความจำ เมื่อคุณควบคุมความเท่าเทียมกันกับ==มันหมายความว่าถ้าอินสแตนซ์ของพวกเขาจะเท่ากัน ไม่จำเป็นต้องเป็นอินสแตนซ์เดียวกันเพื่อให้เท่ากัน สำหรับสิ่งนี้คุณต้องให้เกณฑ์ความเท่าเทียมกับคลาสที่คุณกำหนดเอง โดยค่าเริ่มต้นเรียนที่กำหนดเองและโครงสร้างไม่ได้รับการเริ่มต้นใช้งานของผู้ประกอบการที่สมดุลที่รู้จักในฐานะ“เท่ากับ” ผู้ประกอบการ==และ“ไม่เท่ากับ” !=ผู้ประกอบการ ในการทำเช่นนี้คลาสที่กำหนดเองของคุณต้องเป็นไปตาม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!


248

!==และ===เป็นตัวดำเนินการเอกลักษณ์และใช้เพื่อตรวจสอบว่าวัตถุสองรายการมีการอ้างอิงเดียวกันหรือไม่

Swift ยังมีตัวดำเนินการเอกลักษณ์สองตัว (=== และ! ==) ซึ่งคุณใช้เพื่อทดสอบว่าวัตถุสองรายการอ้างอิงทั้งสองอ้างถึงอินสแตนซ์วัตถุเดียวกันหรือไม่

ข้อความที่ตัดตอนมาจาก: Apple Inc. “ ภาษาการเขียนโปรแกรม Swift” iBooks https://itun.es/us/jEUH0.l


49
ได้. มาจาก ObjC ==คือisEqual:หรือเทียบเท่าระดับความหมายที่กำหนด ===ใน Swift อยู่==ใน (Obj) C - ตัวชี้ความเท่าเทียมกันหรือตัวตนของวัตถุ
rickster

ค่า @rickster Dont 'ยังมีตำแหน่งหน่วยความจำด้วยหรือไม่ ในที่สุดฉันก็อยู่ในความทรงจำ คุณไม่สามารถเปรียบเทียบสิ่งเหล่านั้นได้หรือ หรือเป็นที่ตั้งของหน่วยความจำของพวกเขาไม่ได้มีการใด ๆที่มีความหมายค่า?
ฮันนี่

2
มีอย่างน้อยสองวิธีในการคิดเกี่ยวกับวิธีที่ภาษากำหนดประเภทของค่าและหน่วยความจำ หนึ่งคือการผูกแต่ละชื่อ ( varหรือlet) ของค่าเป็นสำเนาที่ไม่ซ้ำกันดังนั้นจึงไม่มีความหมายในการสร้างพอยน์เตอร์เนื่องจากค่าที่คุณทำตัวชี้ไปยังเป็นค่าที่แตกต่างจากที่คุณสร้างขึ้นครั้งแรก อีกประการหนึ่งคือคำจำกัดความของค่าความหมายของสวิฟท์ย่อมาจากหน่วยความจำ - คอมไพเลอร์มีอิสระในการเพิ่มประสิทธิภาพสูงสุดและรวมถึงไม่เก็บค่าของคุณในตำแหน่งหน่วยความจำที่เข้าถึงได้เกินกว่าบรรทัดที่ใช้
rickster

62

ในทั้งสอง Objective-C และสวิฟท์ที่==และ!=การทดสอบผู้ประกอบการเพื่อความเท่าเทียมกันค่าสำหรับค่าตัวเลข (เช่นNSInteger, NSUInteger, intใน Objective-C และInt, UIntฯลฯ ในสวิฟท์) สำหรับวัตถุ (NSObject / NSNumber และ subclasses ใน Objective-C และประเภทการอ้างอิงใน Swift) ==และ!=ทดสอบว่าวัตถุ / ประเภทการอ้างอิงเป็นสิ่งเดียวกันเหมือนกัน - เช่นค่าแฮชเดียวกัน - หรือไม่เหมือนกันตามลำดับ .

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

สวิฟท์ตัวตนของความเท่าเทียมกันผู้ประกอบการ===และ!==ตรวจสอบความเสมอภาคอ้างอิง - จึงควรอาจจะเรียกว่าความเสมอภาคอ้างอิงประกอบการ IMO

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

3
ขออภัย แต่ใน Obj-C โอเปอเรเตอร์ == ไม่ได้เปรียบเทียบกับความเสมอภาค แต่ค่อนข้างจะเหมือนกับ C - เปรียบเทียบการอ้างอิงตัวชี้ (object Identity)
Motti Shneor

==ไม่ได้ทดสอบNSNumberความเท่าเทียมกันใน Objective-C NSNumberเป็นการNSObjectทดสอบตัวตน เหตุผลที่ SOMETIMES ทำงานนั้นเป็นเพราะตัวชี้ / แท็กวัตถุแคช มันจะล้มเหลวสำหรับจำนวนที่มากพอและบนอุปกรณ์ 32- บิตเมื่อเปรียบเทียบตัวอักษรที่ไม่ใช่
Accatyyc

45

ใน swift 3 ขึ้นไป

===(หรือ!==)

  • ตรวจสอบว่าค่าที่มีเหมือนกัน (จุดทั้งที่อยู่หน่วยความจำเดียวกัน)
  • เปรียบเทียบประเภทการอ้างอิง
  • เช่นเดียวกับ==ใน Obj-C (ความเท่าเทียมกันของตัวชี้)

==(หรือ!=)

  • ตรวจสอบว่าค่าที่อยู่เดียวกัน
  • เปรียบเทียบประเภทค่า
  • ชอบค่าเริ่มต้นisEqual:ในพฤติกรรม Obj-C

ที่นี่ฉันเปรียบเทียบสามอินสแตนซ์ (คลาสเป็นประเภทอ้างอิง)

class Person {}

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

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

นอกจากนี้คุณยังสามารถแทนที่isEqual:ใน Swift:override func isEqual(_ object: Any?) -> Bool {}
Thomas Elliot

37

มีรายละเอียดปลีกย่อยที่มี Swifts ===ที่ไปไกลกว่าเพียงตัวชี้เลขคณิต ในขณะที่ Objective-C คุณสามารถเปรียบเทียบตัวชี้สองตัวใด ๆ (เช่นNSObject *) กับ==สิ่งนี้ไม่เป็นความจริงอีกต่อไปใน Swift เนื่องจากประเภทมีบทบาทมากกว่าระหว่างการรวบรวม

สนามเด็กเล่นจะให้คุณ

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

แต่การแปลงที่ชัดเจนทำให้ชัดเจนว่าอาจมีบางอย่างเกิดขึ้น ใน String ด้านของสิ่งที่จะยังคงอยู่ตราบใดที่เราNSString import Cocoaจากนั้นเราก็จะได้

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 สองคลาส แต่การลดการแปลงโดยนัยอาจทำให้ชัดเจนขึ้นอีกเล็กน้อย


2
คุณไม่สามารถใช้ประกอบการเพื่อเปรียบเทียบ=== Intsไม่ได้อยู่ในสวิฟท์ 3
Jakub Truhlář

เมื่อใดก็ตามที่คุณพูดว่า "โครงสร้างใหม่" กำลังถูกสร้างสิ่งที่เกิดขึ้นจริงคือวัตถุใหม่ (ชนิดของคลาส ) กำลังถูกสร้างขึ้น ===ไม่มีความหมายสำหรับ structs เนื่องจากเป็นประเภทค่า โดยเฉพาะอย่างยิ่งมีสามประเภทที่คุณต้องจำไว้: ประเภทตัวอักษรเช่น 1 หรือ "foo" ซึ่งไม่ได้ผูกกับตัวแปรและโดยทั่วไปจะมีผลกับการรวบรวมเท่านั้น ประเภท struct เช่นIntและStringซึ่งเป็นสิ่งที่คุณได้รับเมื่อคุณกำหนดตัวอักษรให้กับตัวแปรและชั้นเรียนเช่นและAnyObject NSString
saagarjha

12

ตัวอย่างเช่นถ้าคุณสร้างสองอินสแตนซ์ของคลาสเช่นmyClass:

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

คุณสามารถเปรียบเทียบอินสแตนซ์เหล่านั้นได้

if inst1 === inst2

อ้างถึง:

ซึ่งคุณใช้ในการทดสอบว่าวัตถุสองรายการอ้างอิงทั้งสองอ้างถึงอินสแตนซ์วัตถุเดียวกัน

ข้อความที่ตัดตอนมาจาก: Apple Inc. “ ภาษาการเขียนโปรแกรม Swift” iBooks https://itun.es/sk/jEUH0.l


11

ใน Swift เรามี=== simbol ซึ่งหมายความว่าวัตถุทั้งสองอ้างถึงที่อยู่เดียวกันอ้างอิงเดียวกัน

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

เป็นเพียงการช่วยเหลือเล็กน้อยที่เกี่ยวข้องกับAnyวัตถุ

ฉันทำงานกับการทดสอบหน่วยรอบ ๆNotificationCenterซึ่งใช้Anyเป็นพารามิเตอร์ที่ฉันต้องการเปรียบเทียบเพื่อความเท่าเทียมกัน

อย่างไรก็ตามเนื่องจากAnyไม่สามารถใช้ในการดำเนินการที่เท่าเทียมกันจึงจำเป็นต้องเปลี่ยนแปลง ในที่สุดฉันตัดสินด้วยวิธีการต่อไปนี้ซึ่งทำให้ฉันได้รับความเสมอภาคในสถานการณ์เฉพาะของฉันแสดงที่นี่พร้อมตัวอย่างง่าย ๆ :

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

ฟังก์ชั่นนี้ใช้ประโยชน์จากObjectIdentifierซึ่งให้ที่อยู่เฉพาะสำหรับวัตถุทำให้ฉันทดสอบ

หนึ่งรายการที่ควรทราบเกี่ยวกับObjectIdentifierแอปเปิลที่ลิงค์ด้านบน:

ใน Swift เฉพาะอินสแตนซ์ของคลาสและ metatypes เท่านั้นที่มีตัวตนที่ไม่ซ้ำกัน ไม่มีแนวคิดเกี่ยวกับเอกลักษณ์ของ structs, enums, function หรือ tuples


2

==ใช้เพื่อตรวจสอบว่าตัวแปรสองตัวมีค่าเท่ากัน 2 == 2หรือไม่ แต่ในกรณีที่===มันหมายถึงความเท่าเทียมกันคือถ้าสองกรณีที่อ้างถึงตัวอย่างวัตถุเดียวกันในกรณีของการเรียนการสร้างการอ้างอิงที่ถูกจัดขึ้นโดยอินสแตนซ์อื่น ๆ อีกมากมาย


1

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!'

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.