In swift there seem to be two equality operators: the double equals (==
) and the triple equals (===
), what is the difference between the two?
This question is related to
swift
Swift 4: Another example using Unit Tests which only works with ===
Note: Test below fails with ==, works with ===
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)
}
And the class being
class ViewControllerUnderTest: UIViewController, UITextFieldDelegate {
@IBOutlet weak var inputTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
inputTextField.delegate = self
}
}
The error in Unit Tests if you use == is, Binary operator '==' cannot be applied to operands of type 'UITextFieldDelegate?' and 'ViewControllerUnderTest!'
The right shift logical operator (>>> N
) shifts bits to the right by N positions, discarding the sign bit and padding the N left-most bits with 0's. For example:
-1 (in 32-bit): 11111111111111111111111111111111
after a >>> 1
operation becomes:
2147483647: 01111111111111111111111111111111
The right shift arithmetic operator (>> N
) also shifts bits to the right by N positions, but preserves the sign bit and pads the N left-most bits with 1's. For example:
-2 (in 32-bit): 11111111111111111111111111111110
after a >> 1
operation becomes:
-1: 11111111111111111111111111111111
==
is used to check if two variables are equal i.e
2 == 2
. But in case of ===
it stands for equality i.e if two instances referring to the same object example in case of classes a reference is created which is held by many other instances.
===
(or !==
)==
in Obj-C (pointer equality).==
(or !=
)isEqual:
in Obj-C behavior.Here I compare three instances (class is a reference type)
class Person {}
let person = Person()
let person2 = person
let person3 = Person()
person === person2 // true
person === person3 // false
The logical right shift (v >>> n
) returns a value in which the bits in v
have been shifted to the right by n
bit positions, and 0's are shifted in from the left side. Consider shifting 8-bit values, written in binary:
01111111 >>> 2 = 00011111
10000000 >>> 2 = 00100000
If we interpret the bits as an unsigned nonnegative integer, the logical right shift has the effect of dividing the number by the corresponding power of 2. However, if the number is in two's-complement representation, logical right shift does not correctly divide negative numbers. For example, the second right shift above shifts 128 to 32 when the bits are interpreted as unsigned numbers. But it shifts -128 to 32 when, as is typical in Java, the bits are interpreted in two's complement.
Therefore, if you are shifting in order to divide by a power of two, you want the arithmetic right shift (v >> n
). It returns a value in which the bits in v
have been shifted to the right by n
bit positions, and copies of the leftmost bit of v are shifted in from the left side:
01111111 >> 2 = 00011111
10000000 >> 2 = 11100000
When the bits are a number in two's-complement representation, arithmetic right shift has the effect of dividing by a power of two. This works because the leftmost bit is the sign bit. Dividing by a power of two must keep the sign the same.
!==
and ===
are identity operators and are used to determine if two objects have the same reference.
Swift also provides two identity operators (=== and !==), which you use to test whether two object references both refer to the same object instance.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l
They are both right-shift, but >>>
is unsigned
From the documentation:
The unsigned right shift operator ">>>" shifts a zero into the leftmost position, while the leftmost position after ">>" depends on sign extension.
>>>
is unsigned-shift; it'll insert 0. >>
is signed, and will extend the sign bit.
The shift operators include left shift
<<
, signed right shift>>
, and unsigned right shift>>>
.The value of
n>>s
isn
right-shifteds
bit positions with sign-extension.The value of
n>>>s
isn
right-shifteds
bit positions with zero-extension.
System.out.println(Integer.toBinaryString(-1));
// prints "11111111111111111111111111111111"
System.out.println(Integer.toBinaryString(-1 >> 16));
// prints "11111111111111111111111111111111"
System.out.println(Integer.toBinaryString(-1 >>> 16));
// prints "1111111111111111"
To make things more clear adding positive counterpart
System.out.println(Integer.toBinaryString(121));
// prints "1111001"
System.out.println(Integer.toBinaryString(121 >> 1));
// prints "111100"
System.out.println(Integer.toBinaryString(121 >>> 1));
// prints "111100"
Since it is positive both signed and unsigned shifts will add 0 to left most bit.
1 >>> 32 == 1
There are subtleties with Swifts ===
that go beyond mere pointer arithmetics. While in Objective-C you were able to compare any two pointers (i.e. NSObject *
) with ==
this is no longer true in Swift since types play a much greater role during compilation.
A Playground will give you
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))
With strings we will have to get used to this:
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
but then you can also have fun as follows:
var st4 = st // "123"
st4 == st // true
st4 += "5" // "1235"
st4 == st // false, not quite a reference, copy on write semantics
I am sure you can think of a lot more funny cases :-)
Update for Swift 3 (as suggested by the comment from Jakub Truhlár)
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)
This looks a little more consistent with Type 'Int' does not conform to protocol 'AnyObject'
, however we then get
type(of:(1 as AnyObject)) // _SwiftTypePreservingNSNumber.Type
but the explicit conversion makes clear that there might be something going on.
On the String-side of things NSString
will still be available as long as we import Cocoa
. Then we will have
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
It is still confusing to have two String classes, but dropping the implicit conversion will probably make it a little more palpable.
>>>
will always put a 0 in the left most bit, while >>
will put a 1 or a 0 depending on what the sign of it is.
Just a minor contribution related to the Any
object.
I was working with unit tests around NotificationCenter
, which makes use of Any
as a parameter that I wanted to compare for equality.
However, since Any
cannot be used in an equality operation, it was necessary to change it. Ultimately, I settled on the following approach, which allowed me to get equality in my specific situation, shown here with a simplistic example:
func compareTwoAny(a: Any, b: Any) -> Bool {
return ObjectIdentifier(a as AnyObject) == ObjectIdentifier(b as AnyObject)
}
This function takes advantage of ObjectIdentifier, which provides a unique address for the object, allowing me to test.
One item to note though about ObjectIdentifier
per Apple at the above link:
In Swift, only class instances and metatypes have unique identities. There is no notion of identity for structs, enums, functions, or tuples.
>>
is arithmetic shift right, >>>
is logical shift right.
In an arithmetic shift, the sign bit is extended to preserve the signedness of the number.
For example: -2 represented in 8 bits would be 11111110
(because the most significant bit has negative weight). Shifting it right one bit using arithmetic shift would give you 11111111
, or -1. Logical right shift, however, does not care that the value could possibly represent a signed number; it simply moves everything to the right and fills in from the left with 0s. Shifting our -2 right one bit using logical shift would give 01111111
.
In both Objective-C and Swift, the ==
and !=
operators test for value equality for number values (e.g., NSInteger
, NSUInteger
, int
, in Objective-C and Int
, UInt
, etc. in Swift). For objects (NSObject/NSNumber and subclasses in Objective-C and reference types in Swift), ==
and !=
test that the objects/reference types are the same identical thing -- i.e., same hash value -- or are not the same identical thing, respectively.
let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true
Swift's identity equality operators, ===
and !==
, check referential equality -- and thus, should probably be called the referential equality operators IMO.
a === b // false
a === c // true
It's also worth pointing out that custom reference types in Swift (that do not subclass a class that conforms to Equatable) do not automatically implement the equal to operators, but the identity equality operators still apply. Also, by implementing ==
, !=
is automatically implemented.
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
These equality operators are not implemented for other types such as structures in either language. However, custom operators can be created in Swift, which would, for example, enable you to create an operator to check equality of a 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
For example, if you create two instances of a class e.g. myClass
:
var inst1 = myClass()
var inst2 = myClass()
you can compare those instances,
if inst1 === inst2
cited:
which you use to test whether two object references both refer to the same object instance.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/sk/jEUH0.l
In Swift we have === simbol which means is both objects are referring to the same reference same address
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
Read more about Bitwise and Bit Shift Operators
>> Signed right shift
>>> Unsigned right shift
The bit pattern is given by the left-hand operand, and the number of positions to shift by the right-hand operand. The unsigned right shift operator >>>
shifts a zero into the leftmost position,
while the leftmost position after >>
depends on sign extension.
In simple words >>>
always shifts a zero into the leftmost position whereas >>
shifts based on sign of the number i.e. 1 for negative number and 0 for positive number.
For example try with negative as well as positive numbers.
int c = -153;
System.out.printf("%32s%n",Integer.toBinaryString(c >>= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c <<= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c >>>= 2));
System.out.println(Integer.toBinaryString(c <<= 2));
System.out.println();
c = 153;
System.out.printf("%32s%n",Integer.toBinaryString(c >>= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c <<= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c >>>= 2));
System.out.printf("%32s%n",Integer.toBinaryString(c <<= 2));
output:
11111111111111111111111111011001
11111111111111111111111101100100
111111111111111111111111011001
11111111111111111111111101100100
100110
10011000
100110
10011000
Source: Stackoverflow.com