Швидко, здається, є два оператори рівності: подвійний дорівнює ( ==
) і потрійний дорівнює ( ===
), яка різниця між ними?
Швидко, здається, є два оператори рівності: подвійний дорівнює ( ==
) і потрійний дорівнює ( ===
), яка різниця між ними?
Відповіді:
Коротко:
==
оператор перевіряє, чи рівні їх екземпляри рівні, "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!
!==
і ===
є операторами ідентичності та використовуються для визначення того, чи мають два об'єкти однакові посилання.
Swift також пропонує два оператори ідентичності (=== і! ==), які ви використовуєте для перевірки, чи посилаються дві посилання на об'єкт на один і той же екземпляр об'єкта.
Уривок: Apple Inc. "Мова швидкого програмування". iBooks. https://itun.es/us/jEUH0.l
var
або let
) імені до значення є унікальною копією - тому безглуздо створювати покажчики, оскільки значення, для якого ви зробили вказівник, є іншим значенням, ніж те, яке ви створили вперше. Інше полягає в тому, що визначення семантики значення Свіфта абстрагує сховище - компілятор вільний для оптимізації, до і включення ніколи не зберігаючи своє значення в пам'яті, доступній за межами лінії, де воно використовується (реєстр, кодування інструкцій тощо).
В обох 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
==
не перевіряє NSNumber
рівність у Objective-C. NSNumber
це NSObject
так він перевіряє на справжність. Причина, по якій працює SOMETIMES, полягає в тегованих покажчиках / кешованих літералах об'єктів. Він вийде з ладу для досить великої кількості та на 32-бітних пристроях при порівнянні нелітералів.
===
(або !==
)==
у 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 {}
Є тонкощі зі Свіфт, ===
які виходять за межі простої арифметики вказівника. Хоча в 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, але відмова від неявного перетворення, ймовірно, зробить його трохи більш відчутним.
===
оператора для порівняння Ints
. Не в Швидкому 3.
===
не має сенсу для структур, оскільки вони є типовими значеннями. Зокрема, потрібно пам’ятати про три типи: буквальні типи, такі як 1 або «foo», які не прив’язані до змінної і зазвичай впливають лише на компіляцію, оскільки ви, як правило, не працюєте з ними під час виконання; типи структури, такі як Int
і String
які - це те, що ви отримуєте при призначенні літеральної змінної та класів, таких як AnyObject
і NSString
.
Наприклад, якщо ви створюєте два екземпляри класу, наприклад myClass
:
var inst1 = myClass()
var inst2 = myClass()
ви можете порівняти ці екземпляри,
if inst1 === inst2
цитується:
який ви використовуєте для перевірки, чи посилаються дві посилання на об'єкт на один і той же екземпляр об'єкта.
Уривок: Apple Inc. "Мова швидкого програмування". iBooks. https://itun.es/sk/jEUH0.l
У 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
Просто незначний внесок, пов’язаний із Any
об’єктом.
Я працював з одиничними тестами навколо NotificationCenter
, що використовує Any
параметр, який я хотів порівняти для рівності.
Однак, оскільки Any
не може бути використаний в операції рівності, його потрібно було змінити. Зрештою, я зупинився на наступному підході, який дозволив мені отримати рівність у моїй конкретній ситуації, показаному тут на спрощеному прикладі:
func compareTwoAny(a: Any, b: Any) -> Bool {
return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}
Ця функція використовує перевагу ObjectIdentifier , який забезпечує унікальну адресу для об'єкта, що дозволяє мені протестувати.
Один предмет, який слід зазначити, хоча і щодо ObjectIdentifier
Apple, за посиланням вище:
У Swift унікальні ідентичності мають лише екземпляри та метатипи класу. Не існує поняття ідентичності для структур, перерахунків, функцій чи кортежів.
==
використовується для перевірки рівності двох змінних, тобто
2 == 2
. Але у випадку ===
це означає рівність, тобто якщо два екземпляри, що посилаються на один і той же приклад об'єкта, у випадку класів створюється посилання, яке зберігається багатьма іншими екземплярами.
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!'
==
цеisEqual:
або визначена класом смислова еквівалентність.===
у Swift знаходиться==
в (Obj) C - рівність покажчика, або тотожність об'єкта.