==와 ===의 차이점


300

신속하게 두 개의 항등 연산자가있는 것 같습니다 : double equals ( ==) 및 triple equals ( ===), 둘 사이의 차이점은 무엇입니까?

답변:


149

한마디로 :

== 연산자는 인스턴스 값이 같은지 확인합니다. "equal to"

=== 연산자는 참조가 동일한 인스턴스를 가리키는 지 확인합니다. "identical to"

긴 답변 :

클래스는 참조 유형이며, 여러 상수 및 변수가 배후에서 클래스의 동일한 단일 인스턴스를 참조 할 수 있습니다. 클래스 참조는 런타임 스택 (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!


248

!=====신원 연산자이며, 두 물체가 동일한 부호를 가지고 있는지 확인하기 위해 사용된다.

Swift는 또한 두 개의 ID 연산자 (=== 및! ==)를 제공합니다.이 연산자는 두 개의 객체 참조가 모두 동일한 객체 인스턴스를 참조하는지 여부를 테스트하는 데 사용합니다.

발췌 : Apple Inc.“Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l


49
예. ObjC, ==is isEqual:또는 클래스 정의 의미 론적 동등성에서 제공됩니다. ===Swift는 ==(Obj) C — 포인터 평등 또는 객체 동일성에 있습니다.
rickster 2016 년

@rickster Dont '값에도 메모리 위치가 있습니까? 나는 결국 그들은 기억의 어딘가에있다. 당신은 그것들을 비교할 수 없습니까? 아니면 메모리 위치가 의미있는 가치를 제공하지 않습니까?
Honey

2
언어가 값 유형과 메모리를 어떻게 정의하는지에 대해 생각하는 방법에는 적어도 두 가지가 있습니다. 하나는 값에 대한 이름의 각 바인딩 ( var또는 let)이 고유 한 사본이라는 점입니다. 따라서 포인터를 만든 값이 처음 만든 값과 다른 값이므로 포인터를 만드는 것은 의미가 없습니다. 다른 하나는 Swift의 가치 의미론의 정의가 스토리지를 추상화한다는 것입니다. 컴파일러는 값을 사용하는 라인 (레지스터, 명령어 인코딩 등)을 넘어 액세스 가능한 메모리 위치에 값을 저장하지 않는 등 자유롭게 최적화 할 수 있습니다.
rickster

62

두 오브젝티브 C 및 신속한에서, ==!=숫자 값 값 어떤지 연산자 시험 (예를 들어, NSInteger, NSUInteger, int, 및 목표-C에서 Int, UInt등 신속한에서). 개체 (NSObject의 /의 NSNumber 및 서브 오브젝티브 C 및 신속한 참조 유형) 내용 ==!=즉, 동일한 해시 값 - - 또는 각각 같은 동일한 것이 아니다 개체 / 참조 형식이 동일한 동일한 것이 있는지 테스트 .

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

Swift의 항등 평등 연산자 ===!==참조 동등성을 확인하십시오. 따라서 참조 평등 연산자 IMO 라고합니다 .

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

Swift의 사용자 정의 참조 유형 (Equatable을 준수하는 클래스를 서브 클래스 화하지 않음)은 연산자와 동일하게 자동으로 구현되지 않지만 ID 동등성 연산자는 여전히 적용됨을 지적 할 가치가 있습니다. 또한를 구현 ==하면 !=자동으로 구현됩니다.

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에서 == 연산자는 EQUALITY를 비교하지 않지만 C와 마찬가지로 포인터 참조 (객체 ID)를 비교합니다.
Motti Shneor

==NSNumberObjective-C에서 동등성을 테스트하지 않습니다 . NSNumberNSObject그 정체성에 대한 테스트 때문에. SOMETIMES가 작동하는 이유는 태그가 지정된 포인터 / 캐시 된 객체 리터럴 때문입니다. 리터럴이 아닌 문자를 비교할 때 충분히 큰 숫자와 32 비트 장치에서는 실패합니다.
Accatyyc

45

스위프트 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

===단순한 포인터 산술을 넘어서는 스위프트에는 미묘한 점이 있습니다 . 오브젝티브 C에 어떤 두 개의 포인터 (즉 비교할 수 있었지만 NSObject *포함) ==이 유형은 컴파일시 훨씬 더 큰 역할을하기 때문에 더 이상 사실 스위프트에 없습니다.

놀이터는 당신에게 줄 것이다

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ář

"새로운 구조체"가 생성 될 때마다 실제로 일어나고있는 것은 ( 클래스 유형의) 새로운 객체 가 생성되는 것입니다. ===구조체는 가치 유형이므로 구조체에는 의미가 없습니다. 특히, 기억해야 할 세 가지 유형이 있습니다. 1 또는 "foo"와 같은 리터럴 유형은 변수에 바인딩되지 않았으며 일반적으로 런타임 동안 처리하지 않으므로 컴파일에만 영향을 미칩니다. 같은 구조체 유형 IntString당신이 때 할당 리터럴 변수, 클래스와 같은 무엇을 얻을 수 있습니다 AnyObjectNSString.
saagarjha

12

예를 들어 클래스의 두 인스턴스를 만드는 경우 myClass:

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

해당 인스턴스를 비교할 수 있습니다.

if inst1 === inst2

인용 :

두 객체 참조가 모두 동일한 객체 인스턴스를 참조하는지 테스트하는 데 사용합니다.

발췌 : Apple Inc.“Swift Programming Language.” 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위의 링크에서 Apple 당 참고해야 할 항목 하나 :

Swift에서는 클래스 인스턴스와 메타 타입 만 고유 한 ID를 갖습니다. 구조체, 열거 형, 함수 또는 튜플에 대한 동일성 개념은 없습니다.


2

==두 변수가 같은지 확인하는 데 사용됩니다 2 == 2. 그러나 ===동등의 경우, 즉 클래스의 경우 동일한 객체 예제를 참조하는 두 인스턴스가 많은 경우 다른 많은 인스턴스가 보유하는 참조가 작성됩니다.


1

스위프트 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.