Різниця між == і ===


300

Швидко, здається, є два оператори рівності: подвійний дорівнює ( ==) і потрійний дорівнює ( ===), яка різниця між ними?

Відповіді:


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 вказують два різні екземпляри в області Heap, їхні екземпляри рівні, оскільки їх 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 мають два різних екземпляри в області Heap, вони не є тотожними та вихіднимиthe two instance are not identical!

let person3 = person1

P.S: Класи є типовими типами і посилання person1 скопіюється на person3 за допомогою цієї операції призначення, таким чином обидві посилання вказують на один і той же екземпляр в області Heap.

if person3 === person1 {
   print("the two instances are identical!")
}

Вони однакові, і вихід буде the two instances are identical!


248

!==і ===є операторами ідентичності та використовуються для визначення того, чи мають два об'єкти однакові посилання.

Swift також пропонує два оператори ідентичності (=== і! ==), які ви використовуєте для перевірки, чи посилаються дві посилання на об'єкт на один і той же екземпляр об'єкта.

Уривок: Apple Inc. "Мова швидкого програмування". iBooks. https://itun.es/us/jEUH0.l


49
Так. Походить від ObjC, ==це isEqual:або визначена класом смислова еквівалентність. ===у Swift знаходиться ==в (Obj) C - рівність покажчика, або тотожність об'єкта.
рикстер

@rickster Ці значення також мають місце в пам'яті? Я врешті-решт вони десь у пам’яті. Ви ніколи не можете їх порівняти? Або це те, що їх пам'ять не пропонує значущого значення?
Мед

2
Існує щонайменше два способи подумати про те, як мова визначає типи значень та пам'ять. Перша полягає в тому, що кожне прив'язування ( varабо let) імені до значення є унікальною копією - тому безглуздо створювати покажчики, оскільки значення, для якого ви зробили вказівник, є іншим значенням, ніж те, яке ви створили вперше. Інше полягає в тому, що визначення семантики значення Свіфта абстрагує сховище - компілятор вільний для оптимізації, до і включення ніколи не зберігаючи своє значення в пам'яті, доступній за межами лінії, де воно використовується (реєстр, кодування інструкцій тощо).
рикстер

62

В обох Objective-C і Свіфта, то ==і !=тест оператори для значення рівності числових значень (наприклад, NSInteger, NSUInteger, int, в Objective-C і Int, UIntі т.д. в Swift). Для об'єктів (NSObject / NSNumber та підкласи в Objective-C та типи посилань у Swift), ==і !=перевірити, чи об'єкти / типи посилань є однаковою річчю - тобто, однаковою хеш-величиною - або не є однаковою ідентичною річчю відповідно .

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

Оператори рівності ідентичності Свіфта , ===і !==перевіряють референтну рівність - і, таким чином, слід, мабуть, називати референтними операторами рівності ІМО.

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 - порівнює посилання вказівників (Identity object).
Motti Shneor

==не перевіряє NSNumberрівність у Objective-C. NSNumberце NSObjectтак він перевіряє на справжність. Причина, по якій працює 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

Є тонкощі зі Свіфт, ===які виходять за межі простої арифметики вказівника. Хоча в 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 (як запропоновано коментарем Якуба Труляра)

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.
Якуб Трулахр

Щоразу, коли ви кажете, що створюється "нова структура", те, що відбувається насправді, створюється новий об'єкт (типу класу ). ===не має сенсу для структур, оскільки вони є типовими значеннями. Зокрема, потрібно пам’ятати про три типи: буквальні типи, такі як 1 або «foo», які не прив’язані до змінної і зазвичай впливають лише на компіляцію, оскільки ви, як правило, не працюєте з ними під час виконання; типи структури, такі як Intі Stringякі - це те, що ви отримуєте при призначенні літеральної змінної та класів, таких як AnyObjectі NSString.
saagarjha

12

Наприклад, якщо ви створюєте два екземпляри класу, наприклад myClass:

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

ви можете порівняти ці екземпляри,

if inst1 === inst2

цитується:

який ви використовуєте для перевірки, чи посилаються дві посилання на об'єкт на один і той же екземпляр об'єкта.

Уривок: Apple Inc. "Мова швидкого програмування". 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 , який забезпечує унікальну адресу для об'єкта, що дозволяє мені протестувати.

Один предмет, який слід зазначити, хоча і щодо ObjectIdentifierApple, за посиланням вище:

У Swift унікальні ідентичності мають лише екземпляри та метатипи класу. Не існує поняття ідентичності для структур, перерахунків, функцій чи кортежів.


2

==використовується для перевірки рівності двох змінних, тобто 2 == 2. Але у випадку ===це означає рівність, тобто якщо два екземпляри, що посилаються на один і той же приклад об'єкта, у випадку класів створюється посилання, яке зберігається багатьма іншими екземплярами.


1

Swift 4: Ще один приклад використання тестів Unit, який працює лише з ===

Примітка: Тест нижче не вдається з ==, працює з ===

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.