==と===の違い


299

迅速に、2つの等価演算子があるようです:二重の等号(==)と三重の等価(===)、2つの違いは何ですか?

回答:


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参照はヒープ領域内の2つの異なるインスタンスをポイントしますが、それらのインスタンスは、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はヒープ領域に2つの異なるインスタンスを持っているので、それらは同一ではなく、出力は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

!==および===はアイデンティティー演算子であり、2つのオブジェクトが同じ参照を持っているかどうかを判別するために使用されます。

Swiftには2つのID演算子(===および!==)も用意されています。これを使用して、2つのオブジェクト参照が両方とも同じオブジェクトインスタンスを参照しているかどうかをテストします。

抜粋:Apple Inc.「The Swift Programming Language」iBooks。https://itun.es/us/jEUH0.l


49
うん。ObjC、==is isEqual:、またはクラスで定義されたセマンティック同値から来ています。===Swiftは==(Obj)C —ポインタの等価性、またはオブジェクトIDにあります。
rickster

@rickster値にはメモリの場所もありませんか?私は結局、彼らは記憶のどこかにいます。それらを比較することはできませんか?または、それらのメモリ位置が意味のある値を提供していないのですか?
ハニー

2
言語が値の型とメモリをどのように定義するかについて考えるには、少なくとも2つの方法があります。1つは、名前と値の各バインディング(varまたはlet)が一意のコピーであることです。つまり、ポインターを作成した値は、最初に作成した値とは異なるため、ポインターを作成しても意味がありません。もう1つは、Swiftの値のセマンティクスの定義がストレージを抽象化することです。コンパイラーは、使用する行(レジスター、命令のエンコードなど)を超えてアクセス可能なメモリー位置に値を格納することなく、最適化することができます。
rickster

62

両方のObjective-Cとスウィフトで、==および!=数値の値の等価性演算子試験(例えば、NSIntegerNSUIntegerint、Objective-CのとでIntUInt等スイフトで)。オブジェクト(Objective-CのNSObject / NSNumberとサブクラス、==およびSwiftの参照型)の場合!=、オブジェクト/参照型が同じもの(つまり、同じハッシュ値)であるか、それぞれ同じでないことをテストします。 。

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

SwiftのID等価演算子、===および!==、参照等価をチェックします。したがって、おそらく参照等価演算子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では==演算子はEQUALITYを比較しませんが、Cと同様に、ポインター参照(オブジェクトID)を比較します。
Motti Shneor 2017

==NSNumberObjective-Cの同等性をテストしません。NSNumberNSObject、それはアイデンティティのためにテストするようにします。SOMETIMESが機能する理由は、タグ付きポインター/キャッシュされたオブジェクトリテラルが原因です。非リテラルと比較すると、十分な数の32ビットデバイスでは失敗します。
Accatyyc

45

Swift 3以降

===(または!==

  • 値が同一である かどうかをチェックします(両方が同じメモリアドレスを指している)
  • 参照タイプの比較。
  • 以下のような==オブジェクト- C(ポインタ等価)です。

==(または!=

  • 値が同じかどうかを確認します。
  • 値タイプの比較。
  • isEqual:Obj-C動作のデフォルトのように。

ここで3つのインスタンスを比較します (クラスは参照型です)

class Person {}

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

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

また、上書きすることができisEqual:スウィフトに:override func isEqual(_ object: Any?) -> Bool {}
トーマス・エリオット

37

Swifts ===には、単なるポインタ演算を超える微妙な点があります。Objective-Cでは、2つのポインター(つまり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の更新(JakubTruhlářからのコメントで提案)

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

しかし、明示的な変換により、何かが起こっている可能性があることが明らかになります。文字列側では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

2つのStringクラスを持つことはまだ混乱していますが、暗黙の変換を削除すると、おそらく少しわかりやすくなります。


2
===演算子を使用して比較することはできませんInts。Swift 3にはありません
JakubTruhlář2017年

「新しい構造体」が作成されていると言うときはいつでも、実際に起こっていることは(クラス型の)新しいオブジェクトが作成されていることです。===構造体は値型であるため、意味がありません。特に、覚えておかなければならない3つのタイプがあります。1や "foo"などのリテラルタイプは変数にバインドされておらず、通常は実行時に処理しないため、通常はコンパイルにのみ影響します。構造体のようなタイプIntStringあなたが変数にリテラルを割り当てるときに何を得るである、とのようなクラスAnyObjectNSString
saagarjha

12

たとえば、クラスの2つのインスタンスを作成するとします。例myClass

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

それらのインスタンスを比較できます

if inst1 === inst2

引用:

2つのオブジェクト参照が両方とも同じオブジェクトインスタンスを参照しているかどうかをテストするために使用します。

抜粋:Apple Inc.「The 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上記のリンクでアップルごとに注意すべき1つの項目:

Swiftでは、クラスインスタンスとメタタイプのみが一意のIDを持っています。構造体、列挙型、関数、またはタプルのアイデンティティの概念はありません。


2

==2つの変数が等しいかどうかを確認するために使用されます 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.