this piece of code prints two times 'false':
println(levelController?.died)
println(!levelController?.died)
I don't understand why, the levelController is instantiated and the died attribute is declared in LevelController like this:
var died = false
Can someone tell me what I might be doing wrong?
You can create your own not operator like this:
let not = (!)
And now we can assign a bool to test this:
let dead = false
if not(dead) {
print(dead)
// Code
} else {
// Code
}
This is Swift, not Objective C.
levelController?.died is not a boolean value. It is an optional boolean. It can be true, false, or nil. The first println prints false. The logical not operator, applied to an optional, returns false if the optional is not nil, and true if the optional is nil. Just as it does in C when applied to a pointer.
As a complete example, your code:
class Controller {
var died: Bool = false
}
var levelController: Controller? = Controller()
println(levelController?.died)
println(!levelController?.died)
Outputs the following:
Optional(false)
false
The first version of your code levelController?.died makes use of option chaining, unwrapping the levelController and accessing the died property. This explains why the output is Optional(false). You would typically use it as follows:
if let died = levelController?.died {
if (died) {
println("I died")
}
}
The if-let statements unwraps this optional value.
The second version of your code !levelController?.died tests whether the given optional value is nil or not. You will notice that changing died to true of false makes no difference.
However, changing the instantiation as follows:
var levelController: Controller? = nil
Results in !levelController?.died becoming true. This isn't really a terribly practical piece of code!
Related
What is the mechanism of declaring w/o value in Swift5 ? Does the first assign become the real declaration ?
And, should we avoid to declare without value in Swift?
var v:String;
if true {
v = "Hello"
print(v) // print "Hello" when without the print below
}
print(v) // variable 'v' used before being initialized
var v:String="";
if true {
v = "Hello"
print(v) // print "Hello"
}
print(v) // print "Hello"
Well, the message is not very helpful, and that's the problem. This pattern (which I call computed initialization) is perfectly legal and useful and — amazingly — you can even use let instead of var. But you must initialize the uninitialized variable by all possible paths before you use it. So you have:
var v:String
if true {
v = "Hello"
}
print(v) // error
But hold my beer and watch this:
var v:String
if true {
v = "Hello"
} else {
v = "Goodbye"
}
print(v) // fine!
Or even:
let v:String
if true {
v = "Hello"
} else {
v = "Goodbye"
}
print(v) // fine!
Amazing, eh?
Now, you might say: OK, but true will always be true so it's silly to make me fulfill the "all paths" rule. Too bad! The compiler insists anyway, and then lets you off later with a warning that the else won't be executed. But a warning lets you compile; an error doesn't. The truth is that your example is very artificial. But this is a real-life possibility:
let v:String
if self.someBoolProperty {
v = "Hello"
} else {
v = "Goodbye"
}
print(v) // fine!
Not only is this sort of thing legal, it is actually the pattern that Apple recommends under certain slightly tricky circumstances. For instance, it is used in Apple's own example code showing how to use the Swift 5 Result struct:
let result: Result<Int, EntropyError>
if count < AsyncRandomGenerator.entropyLimit {
// Produce numbers until reaching the entropy limit.
result = .success(Int.random(in: 1...100))
} else {
// Supply a failure reason when the caller hits the limit.
result = .failure(.entropyDepleted)
}
So this is because swift compiles your code and notices that your value var v:String; is undeclared before being used which makes it an "optional" value. Even though you are assigning it within the if statement, if you were to get rid of the true value it is possible that the if statement would never run therefore no value will ever be stored in v, thus it would be used before "assigned".
So to answer your question if you want your value to be an optional and possible empty value declare v as the following var v:String? if you would like it to be a non-optional value with a value always stored within v you should declare it as the following var v = "". Swift will interpret this declaration as a String.
To answer your second question, defining without values in swift is 100% allowed, it really just depends on how you want to handle your code. I use optional values in my code all the time, but I don't necessarily like optionals so i usually like to declare my values such as var v = "" that way if the value is empty my UI or whatever else i'm manipulating won't break. But if i need to ensure a value is present i will have to make my value optional so i can use an if statement to check whether it's a valid value or not.
Shorter version of what I'm trying to say is, you are receiving the compiler warning because you are declaring v as a non-optional value rather than an optional value.
I am playing with Arrays in playground and I am bit confused. Here is code:
var players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
print(players.isEmpty) // False
var currentPlayer = players.first // "tob"
print(currentPlayer) // "Optional("tob")\n"
Why does it says "Optional"?
I found explanation: "The property first actually returns an optional, because if the array were empty, first would return nil."
But it is not empty. .isEmpty //false, So I am not understanding this.
Thanks for help in advance.
The correct way to think of Optional is that this may or may not have a value. What is the first element of an empty list? There is no such thing. It is not a value. We call that lack of a value nil or .None.
In Swift a variable must have a specific type. So your example:
let currentPlayer = players.first
What is the type of currentPlayer? It may be a String, or it may be nothing at all. It is a "maybe string" and in Swift that's called an Optional<String>. Whether players has elements or doesn't have elements doesn't change the type of currentPlayer.
If you want to do something if-and-only-if the variable has a value, then there are many ways. The simplest is if-let.
let players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
print(players.isEmpty) // False
if let currentPlayer = players.first {
print(currentPlayer)
}
This will print tob as you're expecting.
Another very common approach is the guard let
let players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
guard let currentPlayer = players.first else { return }
print(currentPlayer)
This lets you avoid nesting the rest of your function inside of curly braces, but otherwise is the same approach.
It is possible to convert an Optional into its underlying type using !, but this is very dangerous and should be avoided except where absolutely necessary. Tools like if-let and guard-let (and also Optional.map) are almost always preferred.
But the key here is to understand that all Swift variables have a single type, and sometimes that type is "maybe it has a value, maybe it doesn't."
If we look at the description of first, we will see that it always returns optional type:
public var first: Self.Generator.Element? { get }
Swift allows variables to be declared but not initialized. How can I check if a variable is not initialized in Swift?
class myClass {}
var classVariable: myClass // a variable of class type - not initialized and no errors!
//if classVariable == nil {} // doesn't work - so, how can I check it?
You're right—you may not compare a non-optional variable to nil. When you declare, but do not provide a value for, a non-optional variable, it is not set to nil like an optional variable is. There is no way to test for the use of an uninitialized non-optional variable at runtime, because any possibility of such use is a terrible, compiler-checked programmer error. The only code that will compile is code that guarantees every variable will be initialized before its use. If you want to be able to assign nil to a variable and check its value at runtime, then you must use an optional.
Example 1: Correct Usage
func pickThing(choice: Bool) {
let variable: String //Yes, we can fail to provide a value here...
if choice {
variable = "Thing 1"
} else {
variable = "Thing 2"
}
print(variable) //...but this is okay because the variable is definitely set by now.
}
Example 2: Compilation Error
func pickThing2(choice: Bool) {
let variable: String //Yes, we can fail to provide a value here, but...
if choice {
variable = "Thing 1"
} else {
//Uh oh, if choice is false, variable will be uninitialized...
}
print(variable) //...that's why there's a compilation error. Variables ALWAYS must have a value. You may assume that they always do! The compiler will catch problems like these.
}
Example 3: Allowing nil
func pickThing3(choice: Bool) {
let variable: String? //Optional this time!
if choice {
variable = "Thing 1"
} else {
variable = nil //Yup, this is allowed.
}
print(variable) //This works fine, although if choice is false, it'll print nil.
}
It might be a anomaly of the compiler that you don't get an error declaring a variable this way
class MyClass {}
var myClass : MyClass
but in a Playground you get a runtime error when you just read the variable
myClass
variable 'myClass' used before being initialized
One of the most essential features of Swift is that a non-optional variable can never be nil. If you try to access the variable you'll get a runtime error aka crash.
The implicit unwrapping of a Bool type does not seem to work:
var aBoolean: Bool! // nil
aBoolean = false // false
aBoolean // false
aBoolean == true // false
aBoolean == false // true
if aBoolean {
"Hum..." // "Hum..."
} else {
"Normal"
}
if aBoolean! {
"Hum..."
} else {
"Normal" // "Normal"
}
If I had declared aBoolean like var aBoolean: Bool?, this would have been the expected behavior but here, I don't get it.
Is this the correct behavior? I didn't find any doc about it.
Thanks!
The first test is checking whether aBoolean stores a value rather than nil, which it does:
if aBoolean {
"Hum..." // "Hum..."
else {
"Normal"
}
The second test is checking against the actual boolean value stored in aBoolean, which is false:
if aBoolean! {
"Hum..."
} else {
"Normal" // "Normal"
}
This is Illustrated in the Swift book in the "Implicitly Wrapped Optionals" section. I think the implicit unwrapping just doesn't apply with if-statements. I agree it is strange but here is the Apple example:
You can still treat an implicitly unwrapped optional like a normal optional, to check if it contains a value:
let assumedString: String! = "An implicitly unwrapped optional string."
if assumedString {
println(assumedString)
}
// prints "An implicitly unwrapped optional string."
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l
Not an answer, but if this is indeed the intended behavior with implicitly unwrapped booleans it's rather disturbing. As soon as you use the expression in any logic proposition it will get unwrapped:
var aBoolean: Bool! = false
if !aBoolean {
"I'm unwrapping" // "I'm unwrapping"
}
if aBoolean == false {
"I'm unwrapping" // "I'm unwrapping"
}
Say you have this in your code an at some point your model changes and the condition is reversed, you delete the NOT.
if aBoolean {
"I'm unwrapping" // <- ouch
}
You just got screwed. Makes me want to avoid the implicit unwrapping.
You are making two different truth-checks.
Given:
var aBoolean: Bool! // nil
aBoolean = false // false
if aBoolean {
"Hum..." // "Hum..."
else {
"Normal"
}
if aBoolean! {
"Hum..."
} else {
"Normal" // "Normal"
}
...in the first if aBoolean, the value is not being unwrapped, it is simply testing the optional type to determine if it stores a value.
In the second if aBoolean!, you are testing the truth-value of the unwrapped Bool.
To see that it is indeed being implicitly-unwrapped (when not used in a conditional), try:
println("value of implicitly-unwrapped aBoolean: \(aBoolean)")
...this will print 'true' when aBoolean is set to true, 'false' when it is set to false, and 'nil' when it has not yet been assigned.
Here is the behavior clearly shown:
> var bool1 : Bool!
bool1: Bool! = nil
> bool1 = false
> (bool1 ? "yes" : "no")
$R19: (String) = "yes"
In the above, since bool1 is an optional (which becomes a Some instance), the conditional of simply bool1 evaluates to true (it is not nil).
> var bool2 : Bool = false
bool2: Bool = false
> (bool2 ? "yes" : "no")
$R25: (String) = "no"
When bool2 is not an optional, the conditional of simply bool2 evaluates to false (the value of bool2)
When declaring a variable as "implicitly unwrapped", using the !, it is still an Optional
This type of variable behaves very much like the standard variable type in Objective-C. It can be assigned to nil, but when you access members you do not need to explicitly unwrap it. In this way it is "unsafe" just like in ObjC because you can be accessing properties from nil
Thus, it is used most commonly when bridging from Objective-C. However, it is still an optional, with the only difference being you don't need to unwrap it to access the contents.
I believe this was added mostly to support interop between Swift and Obj-C and might be good practice to sparingly use it in pure Swift code. (Swift guidelines are a fuzzy thing right now and someone might prove me wrong soon!)
I'm going through the swift docs, and in the optional segment, it talks about using the question mark -- ? -- to signify variables that might be nil. This can be used in an if statement to check for nil, but in the docs they assign the optional to a new variable in the conditional. Is there a reason for this?
For Example, it is presented in the docs similar to this:
// Declare an optional string (might be nil)
var optionalString: String? = "Hello"
// Assigns optionalString to new variable before checking if nil
if let string = optionalString {
println("\(optionalString) is not nil!")
}
else {
println("\(optionalString) is nil")
}
However, this runs just fine for me in tests:
var optionalString: String? = "Hello"
// Assigns optionalString to new variable before checking if nil
if optionalString {
println("\(optionalString) is not nil!")
}
else {
println("\(optionalString) is nil")
}
Question
Is there a reason to assign optionalString to a new variable string in the conditional statement?
Take a look at the section on Optional Chaining in the docs. In the example you cite, there's not much difference. But in other cases, an if-let construction lets you get at an unwrapped value that comes from a series of optional references and method calls, without using implicit unwraps that can crash your app if you haven't considered all the possible bindings for a value in a chain.
It's also useful if you want to avoid recomputing a value. You can use it in a lot of the same ways you'd use an assignment in a conditional in (Obj)C (remember if (self = [super init])).
For example, if the optional being tested comes from a computed property:
var optionalName: String? {
get {
if checkTouchID() {
return "John Appleseed"
} else {
return nil
}
}
}
var greeting = "Hello!"
if optionalName != nil {
greeting = "Hello, \(optionalName)"
}
Paste that into a playground, along with a stub implementation of checkTouchID() that returns true, and you'll immediately see in the results area that the optionalName getter is executing twice. (This would be a problem in a more realistic scenario, because you probably don't want code like this to implicitly checkTouchID() or downloadFromServer() or billApplePay() twice.) If you use an if-let construction instead, you'll only execute the getter once.
In a series of chained optionals (like if let johnsStreet = john.residence?.address?.street in the docs linked above), you don't want to rewrite the whole chain in the body of the if statement, much less recompute it.
I think the purpose of that assignment was to demonstrate the use of "let" within the if conditional clause. I don't see a meaningful difference between the provided code and your own.
From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/il/jEUH0.l
“If the optional value is nil, the conditional is false and the code in braces is skipped. Otherwise, the optional value is unwrapped and assigned to the constant after let, which makes the unwrapped value available inside the block of code.”