Can some one please explain to me how it works
var count: Int?
count = 1
if let count = count {
//do something.
}
Why there is no compiler error at if let count = count as we already created a variable named count as var count: Int?. How come two variables with same name possible?
Swift treated as both variables as a different one. The "count" that declared first can be treated as global one while the constant "count" is only available inside if condition, so it can't access outside if condition.
Optional Variable: It can contain a value or a Nil value. Nil represents the absence of a value or nothing
var count: Int? // Optional Variable
Here variable count is global variable.
Optional Binding: It is the way by which we try to retrieve a values from a chain of optional variable.
if let count = count {
//do something.
}
Here constant count value is available only with in the scope. It cannot be accessed outside the scope.
This is called optional binding
if let constantName = someOptional {
statements
}
someOptional is checked to see if it's nil or has data. If it's nil, the if-statement just doesn't get executed. If there's data, the data gets unwrapped and assigned to constantName for the scope of the if-statement. Then the code inside the braces is executed.
Related
I am running the below code -
class Element {
var name: String
init(name: String) {
self.name = name
}
deinit {
print("Element is deinitializing...")
}
}
var element: Element? = Element(name: "Silver")
var closure = {
print(element?.name ?? "default value")
}
print(isKnownUniquelyReferenced(&element))
element?.name = "Gold"
element = nil
closure()
and it prints -
true
Element is deinitializing...
default value
In the above, isn't the closure captures element strongly? How the element is getting nil inside the closure?
From Swift Programming Guide - Closures
A closure can capture constants and variables from the surrounding context in which it’s defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.
A closure captures variables, not the contents of variables. When we are talking about local variables in a function (which are normally allocated on stack), it makes sure they are accessible even when the function exits and other local variables are deallocated, therefore we can do things like this:
func myFunc() {
var array: [Int] = []
DispatchQueue.main.async {
// executed when myFunc has already returned!
array.append(10)
}
}
Your example is similar. The closure captures the variable. This is a variable on module level, therefore its scope always exists. When you reassign its value, it will affect the value read inside the closure.
Or, in other words, the closure will be equivalent to:
var closure = {
print(CurrentModule.element?.name ?? "default value")
}
where CurrentModule is the name of your main module (which is usually the name of your project).
To prevent this behavior and capture the value of the variable instead, we can use closure capture list. Unfortunately, the official documentation does not properly explain what exactly is a capture list. Basically, using a capture list you declare variables local to the closure using values that are available when the closure is created.
For example:
var closure = { [capturedElement = element] in
print(capturedElement?.name ?? "default value")
}
This will create a new variable capturedElement inside the closure, with the current value of variable element.
Of course, usually we just write:
var closure = { [element] in
print(element?.name ?? "default value")
}
which is a shorthand for [element = element].
I have created my own class in Swift as below.
class Product: NSObject {
var product_id:Int?
var product_number:String?
var product_price:Float?
var product_descrption:String?
}
Now i am setting value in each property like this
let p=Product()
p.product_id=1
p.product_price=220.22
p.productdescrption="Some description"
p.product_number="W2_23_233"
But when i get the value from price then for price i get value like "Optional 220.22" But i don't get appended word "Optional" in description".So to resolve this i added "!" for unwrapping the value of float but i did not have to do this for String please tell why this is happening?
If you are printing any of these values should say Optional(...). If you are assigning the values to a label, that will not include the Optional(...), The reason that it shows Optional(...) when you print the value using print(), is just to show you its an optional. For safety, instead of using the !, try using if lets.
An example with your code,
if let id = p.product_id {
print(id) //Does not contain Optional()
}
You can also combine them, to do them all at one time. (Only do this if you don't want to print unless all values are non-nil)
if let id = p.product_id,
let price = p.product_price,
let description = p.productdescrption,
let productNumber = p.product_number {
//Enter code here that does something with these values
}
Note, if you aren't on swift 3, I believe you only have to write let on the first condition.
If you print any optional variable without unwrapping no matter what type it is, Optional will be appended to the variable's value.
print(p.product_price) will print Optional(220.220001)
print(p.product_descrption) will print Optional("Some description")
To print only value you need to unwrap the optional variables.
print(p.product_price!) will print 220.22
print(p.product_descrption!) will print Some description
This forced unwrapping will only work if the optionals does not contain nil. Otherwise it will give you a runtime error.
So to check for nil you can use if let statement.
No matter what type of variable. If you assign a value to an optional variable, It always enclosed with Optional(...)
Optional without forced unwrapping:
print("product_price = \(p.product_price) \n product_descrption = \(p.product_descrption)")
Output:
product_price = Optional(220.22)
product_descrption = Optional(Some description)
Optional with forced unwrapping:
print("product_price = \(p.product_price!) \n product_descrption = \(p.product_descrption!)")
Output:
product_price = 220.22
product_descrption = Some description
I am writing swift code & having a simple problem.
I declared a MSMutableArray, under certain condition, I set it to nil:
func doJob() -> NSMutableArray {
var arr = NSMutableArray()
arr = addContentToArray()
if CRITICAL_CONDITION {
//ERROR: Cannot assign a value of type 'nil' to a value of type 'NSMutableArray'
arr = nil
}
return arr
}
But when I set arr to nil, I got compiler error:
Cannot assign a value of type 'nil' to a value of type 'NSMutableArray'
Why? How can I set it to nil then?
You can assign nil only to optional variables. However, when you are letting the type to be inferred, the compiler doesn't know that you are planning to assign nil in the future:
var arr: NSMutableArray? = NSMutableArray()
However, the whole thing about assigning nil to a variable that has previously held an array seems a bit dirty to me. Maybe it would be easier to use a new variable?
You haven't posted your real code so we can't do a real review but:
if CRITICAL_CONDITION {
arr = nil
}
return arr
can be more easily written as
if CRITICAL_CONDITION {
return nil
}
return arr
That will solve the problem, too, because you won't need to reassign the variable. A different approach would be to use a second variable:
var result: NSArray? = array
if CRITICAL_CONDITION {
result = nil
}
return result
or even better
let result = CRITICAL_CONDITION ? nil: array
return result;
The whole point of specifying when a variable cannot be nil (non-optional) is the fact that optional variables are dangerous and you have to check them for nil all the time. So, use optionals only for a short time, ideally one condition and then convert them to non-optional. In this case, use the non-optional as long as possible and only when you really need to assign nil, convert to optional (declare a second, optional variable).
I have defined a String type computed property:
var name : String? {
//an optional variable
var theName : String?
if SOME_CONDITION {
theName = “I have a name”
}
//ERROR: '?'must be followed by a call, member lookup or subscript
return theName?
}
I want to return whatever theName is, if it is nil, return nil. So I use return theName? , I don’t want to have runtime error. The compiler however raise an error '?'must be followed by a call, member lookup or subscript Why? How to get rid of it.
What about this? Looks more elegant to me:
var name : String? {
let condition = true // your own condition here of course
return condition ? "I have a name" : nil
}
The problem in your code:
var name : String? {
var theName : String?
let condition = true // your own condition here of course
if condition {
theName = "I have a name"
}
return theName // get rid of the ? here
}
The field theName is already optional, no need to add another ? there.
Why is my proposed solution not an alternate solution:
The construct I used is called ternary operator:
The ternary conditional operator is a special operator with three parts, which takes the form question ? answer1 : answer2. It is a shortcut for evaluating one of two expressions based on whether question is true or false. If question is true, it evaluates answer1 and returns its value; otherwise, it evaluates answer2 and returns its value.
It behaves like the if statement but is suitable here as it is shorter and thus clearer to read: Depending on the condition, the value is either theName or nil. You really don't need to assign the value to any other variable, because, afterall, you are computing it, so might as well simply return it as the condition decides, what the value is.
Adding ? to the end of a type makes it Optional.
Adding ? to the end of an optional variable invokes Optional Chaining.
You specify optional chaining by placing a question mark (?) after the
optional value on which you wish to call a property, method or
subscript if the optional is non-nil. This is very similar to placing
an exclamation mark (!) after an optional value to force the
unwrapping of its value. The main difference is that optional chaining
fails gracefully when the optional is nil, whereas forced unwrapping
triggers a runtime error when the optional is nil.
To reflect the fact that optional chaining can be called on a nil
value, the result of an optional chaining call is always an optional
value, even if the property, method, or subscript you are querying
returns a non-optional value. You can use this optional return value
to check whether the optional chaining call was successful (the
returned optional contains a value), or did not succeed due to a nil
value in the chain (the returned optional value is nil).
Specifically, the result of an optional chaining call is of the same
type as the expected return value, but wrapped in an optional. A
property that normally returns an Int will return an Int? when
accessed through optional chaining.
Example
class Foo {
var bar: Int
}
var x: Foo? // ? is attached to type Foo, it makes x an optional variable
let y: Int? = x?.bar // ? is attached to the variable x, this is optional chaining, it makes .bar return Int?
Why is it that I cannot mutate an implicitly unwrapped optional variable?
Here is a short example the reproduces the issue:
With Array
var list: [Int]! = [1]
list.append(10) // Error here
Immutable value of type '[Int]' only has mutating members named 'append'
With Int
var number: Int! = 1
number = 2
number = 2 + number
number += 2 // Error here
Could not find an overload for '+=' that accepts the supplied arguments
Because the way you are trying to mutate them is by mutating the values (which are immutable) instead of mutating the var.
In Swift value types are immutable. All and always.
Mutation is not a mutation of the value, it's a mutation of the variable that contains the value.
In the case of the Int, the += operator gets a structure on the left and an Int on the right, and it cannot add a structure to an int.
In the case of the Array the append is a mutating member. But it's being invoked on an immutable value that is not directly stored in a variable. It can only operate on values that are directly stored in a variable (which is what makes them mutable: the fact that they are stored in a variable. They are not really mutable, the variable is).
Update:
This has been fixed in Xcode Beta 5 with one small caveat:
var list: [Int]! = [1]
list.append(10)
var number: Int! = 1
number! += 2
number += 2 // compile error
The array works as expected, but it seems that right now the integer still requires an explicit unwrap to allow using +=
Currently, this is just the nature of Optionals (whether implicitly unwrapped or not). The unwrap operator returns an immutable value. This is likely to be fixed or a better solution will be provided in the future.
The only way around it for now is to wrap the array in a class:
class IntArray {
var elements : [Int]
}