Why does the following not work in Swift?
if someObject === nil {
}
You have to do the test using the == operator such as
if someObject == nil {
}
I was thinking that === was more like making sure the instances where the exact same (basically comparing the pointers) and == was more like an isEqual check. Thus I would think === would be more appropriate for testing against nil, but I am obviously incorrect.
The documentation states:
=== or “Identical to” means that two constants or variables of class type
refer to exactly the same class instance.
== or “Equal to” means that two instances are considered “equal” or “equivalent”
in value, for some appropriate meaning of “equal”, as defined by the type’s designer.”
It works exactly like you expect:
var s: String? = nil
s === nil // true
The only caveat is that to compare to nil, your variable must able to be nil. This means that it must be an optional, denoted with a ?.
var s: String is not allowed to be nil, so would therefore always returns false when === compared to nil.
My first instinct would be that nil is not a class instance, but a reference. So someObject cannot be an equivalent class instance to nil.
Related
In Swift 5, what is a way to compare pointers to two closures?
I was told here
In Swift 3, what is a way to compare two closures?
that this is somehow a different question.
So now you have it.
public typealias Action = () -> Void
let action1: Action = {}
let action2: Action = {}
assert(action1 == action1)
assert(action1 != action2)
About pointers: notably, objects (classes) are reference types, so the equality of two objects is not the same as the identity of two objects. For equality (if defined within the class), you have the == operator, and for identity, the === operator:
class Foo:Equatable {
var i:Int = 0
static func == (lhs: Foo, rhs: Foo) -> Bool {
return lhs.i == rhs.i
}
}
let foo1 = Foo()
let foo2 = Foo()
let referenceToFoo1 = foo1
foo1 == foo2 // equality: true, since foo1.i == foo2.i, which is the criteria for equality
foo1 === foo2 // identity: false, since they are two different instances
foo1 === referenceToFoo1 // identity: true, both variables point to the same instance
foo1.i = 2
print(referenceToFoo1.i) // 2, since it refers to the same object
"Reference" can also be called a "pointer," although in Swift, unlike in C, we don't have to delve into pointer arithmetic, which is a pretty low-level way of dealing with memory addresses of pointers to objects and other data.
Just like classes, closures are also reference types in Swift, so in addition to their "equality" (do they contain the same code and captured information etc.) we can also look into their identity (i.e. do these two variables refer to the same closure or two different ones, even if they look the same etc.).
Problem is, Swift doesn't really seem to want to help us there.
Just like == doesn't work for closures, neither does ===. Neither do closure-types seem to conform to any protocol (and note that there's no single "closure type" but rather an infinite multitude of them depending on parameters and return types).
Furthermore, even casting a closure to AnyObject doesn't work:
foo1 as AnyObject === referenceToFoo1 as AnyObject // true, as expected
// with the `action1` closure from the question:
action1 as AnyObject === action1 as AnyObject // false; a bit of a surprise
Looks like every time Swift casts a closure to AnyObject, it creates a new AnyObject instance for some reason… So comparing these also reveals nothing.
So… I don't think we can reason about the identity or equality of closures in Swift. Maybe there is some other way, possibly via unsafe pointers… well, let me know if anyone had any luck going down that rabbit hole!
In swift you can unwrap optional values with a guard statement
guard let foo = foo else { return nil }
Is this statement making a copy of foo? In other words, could this statement be potentially expensive if foo is very large?
Actually this depends on type of foo e.x class type won't create a copy unlike struct type
guard isn't a magic keyword it simply if not presented in 1 word
guard let foo = foo else { return nil }
Could this statement be potentially expensive if foo is very large?
Instead of asking yourself the question that way, ask it this way: what if you had said
if foo != nil {
let foo = foo!
// everything else here
}
Was saying foo! to unwrap the Optional "expensive"? Whatever your answer is, it must be exactly the same for guard let foo = foo, because they both do exactly the same thing: they test for nil and, if safe, they unwrap the Optional. And that is all they both do. The one is merely syntactic sugar for the other.
You have two ways:
1- To only check if this foo does not equal nil
In this case you can check if foo is nil or not
if foo != nil {
// do you work here
}
2- To get information from foo
In this case, you have to unwrap this optional (with guard-let or if-let statements) and here we have two cases:
- if foo is a class, then it’s a reference type. so there will be no copy and a reference to this instance is created
- if foo is a struct, then it’s a value type. so an entirely new instance is created (copied from the original)
Also you can read this answer to see should you choose struct or class in swift
If the property you're unwrapping is a reference type (e.g. a class) then no it will not make a copy, it will just create a new property with a reference to the original instance.
If the property you're unwrapping is a value type (e.g. struct) then it will make a copy.
Why is this variable i not substituted for Bool but optional binding is?
Swift claims this in the guide..
Swift’s type safety prevents non-Boolean values from being substituted
for Bool. The following example reports a compile-time error:
let i = 1
if i {
// this example will not compile, and will report an error
}
Yet this compiles
var foo: String?
if let bar = foo {
print("Non nil")
}
An optional is essentially an enum with two possible values either a designated value, in your example a string, or nil. An if let binding is not being substituted for a Bool in your example. Instead, it is checking if the variable foo is nil and if it is not nil then it sets bar equal to foo and performs the code inside the block. An if let binding is thus essentially a shortcut for
var foo: String?
if foo != nil {
//Set bar equal to the unwrapped value of foo
let bar = foo!
//Do whatever
}
The efficiency of this is best shown in a case where you would want to check if something is nil and check some characteristic of the value if it is not nil. Say you wanted to also check that foo has more than 5 characters and if it does then do something. Since foo could be nil you would have to check for it specifically to ensure it is not. An if let binding will let you do this:
if let bar = foo where bar.characters.count > 5 {
print(bar)
}
rather than this:
if foo != nil && foo?.characters.count > 5 {
let bar = foo!
print(bar)
}
It basically makes very readable, and thus more maintainable code. Also, it prevents you from having to unwrap the optional manually (the ! operator at the end of foo).
I am trying the below code in Playground and I don't quite understand why the compiler doesn't give an error when bar, which is nil, is returned from a non-optional function; and why it does when essentially nil is being compared to nil and gives an error message which seems not correct when claiming that foobar can never be nil.
EDIT:
Since Optional is also Any, why returning nil directly as in foo2 is not allowed?
func foo() -> Any {
let bar: String? = nil
return bar
}
let foobar = foo()
if foobar == nil {
}
func foo2() -> Any {
return nil
}
EDIT: Since Optional is also Any, why returning nil directly as in foo2 is not allowed?
Because nil has no type of its own. It says there's nothing, but nothing of what? You have to pick that type:
func foo2() -> Any {
return nil as String?
}
Even this is legal:
func foo2() -> Any {
return nil as Any?
}
But never, never do this. Any is not a normal tool. It's a last-ditch escape-hatch for the type system to deal with very special problems (like print). When you mix the magic of Any with the magic of Optional (specifically Optional-promotion), you will tend to constantly run into confusing errors and find yourself running the wrong overloads.
AnyObject does come up a bit, but only because of bridges to ObjC, which uses untyped values a lot (though this is improving and AnyObject is becoming less common). But it still rarely makes sense to return an AnyObject in Swift code.
Let's explain exactly what happens in your code:
func foo() -> Any {
let bar: String? = nil
return bar
}
nil is a literal that gets converted to type String? (or Optional<String>), that is, into Optional<String>.None.
Then it is cast to Any. Anything can be cast to Any, including optionals. Basically you have decided to hide the underlining types.
Then foobar == nil. Note that == is an operation that is not automatically available for all types. The comparison with nil is defined on optional types, e.g. for Optional<T> with another Optional<T>.
In this case you have Any and a nil literal. Any itself is not an optional, therefore you cannot compare it directly with nil.
You can cast it to an optional Any.
let optionalFoobar = any as Any?
However, now the variable contains a double optional Optional<Any>.Some(Optional<String>.None) and comparison with nil won't work.
The only thing that would work would be:
if (foobar as Any?) as? String == nil {
print("is nil")
}
In short, don't use Any and if you are using non-optional Any, don't store optional types into it, it will get really complicated and unusable in practice.
Any is not Optional so you cannot compare to nil. If you want to compare to nil, you return type must be an Optional type: Any? for example:
func foo() -> Any? {
let bar: String? = nil
return bar
}
let foobar = foo()
if foobar == nil {
print("foobar is nil")
}
Objective-C has two methods to test if an object is an instance of a specific class or a subclass:
- (BOOL)isMemberOfClass:(Class)aClass;
Returns a Boolean value that indicates whether the receiver is an
instance of a given class.
- (BOOL)isKindOfClass:(Class)aClass;
Returns a Boolean value that indicates whether the receiver is an
instance of given class or an instance of any class that inherits from
that class.
In Swift I can test for the latter by using the is operator:
if myVariable is UIView {
println( "I'm a UIView!")
}
if myVariable is MyClass {
println( "I'm a MyClass" )
}
How can I test if an instance is a specific class or type in Swift (even when dealing with no NSObject subclasses)?
Note: I'm aware of func object_getClassName(obj: AnyObject!) -> UnsafePointer<Int8>.
See my answer at (possible duplicate) https://stackoverflow.com/a/26365978/195691:
It's now possible to compare identity of dynamic types in Swift:
myVariable.dynamicType === MyClass.self
Swift 3+ we can do this using type(of: T) function.
let view = UIView()
if type(of: view) == UIView.self {
print("view isMember of UIView")
}
In addition to object_getClassName(), invariants can be maintained by using a definitional equality in conjunction with object_getClass()
object_getClass(X()) === object_getClass(X()) // true
object_getClass(X()) === object_getClass(Y()) // false