Why I get retain circle in the second example? - swift

Why do we get strong reference circle in the second example, why we did not in the first example?
class Test1 {
var name: String = "Ted"
lazy var greeting = { return "Hello \(self.name)" }()
deinit{print("goodby1")} // No retain cycle here ..
}
var v1:Test1? = Test1()
v1?.greeting
v1 = nil //prints goodby1, dealocation done
class Test {
var name: String = "Mery"
lazy var greeting = {return "Hello \(self.name)"}
deinit{print("goodby")} //retain cycle here
}
var v: Test? = Test()
v!.greeting
v = nil

In the first example, the closure is executed one time, it returns a String, and that String is assigned greeting. There is no closure; nothing is captured. This is just an anonymous function. If you like, you can get rid of the self., just like in a named function. The key point is that the type of greeting is String.
In the second example, greeting is a closure value, which captures self. Since self holds greeting and greeting holds self, there's a loop. The key point is that the type of greeting is () -> String. This is almost certainly a mistake, since this is not how lazy is intended to be used.
(lazy is a pretty weird feature in Swift, and was probably a bad idea to include in the langauge. I try to avoid it as much as possible. There are several subtle ways to use it incorrectly, so it's not surprising that it's bitten you.)

Related

Swift, when referencing a class property, is it making a copy of the data?

So I'm a little confused because of conflicting information, just looking for some clarity regarding memory allocation for Class properties.
So here are my assumptions, please let me know if any of them are wrong:
In Swift, except for Classes and Functions, everything is passed by Value.
Classes instances (objects) are allocated on the Heap
When you pass an object around, you are passing the pointer
When you reference a property on an object, the pointer is dereferenced, and the value of the property is retrieved
So here's my confusion, say my class has a String property, and an Int property. Both Swift data types, that get passed by value in any ordinary situation.
If I ask for let test = object.stringProperty, am I going to get a copy of my string value copied into my test variable?
Similarly, if I had a method inside of my class,
func getAllProperties() -> (String, Int) {
return (self.stringProperty, self.intProperty)
}
is object.getAllProperties() going to return a copy of the properties in a tuple?
I know it seems like a basic question, but after reading several sources I just ended up more uncertain than when I started
Yes and yes. It doesn't matter that the String and the Int were in a class. You asked for the String or the Int (or both), those are value types, you got copies.
It's easy to prove this to yourself, especially with the String. Just change something about it, and then look back at what the class instance is holding: it will be unchanged.
class C {
var stringProperty : String
init(string:String) {
self.stringProperty = string
}
}
let c = C(string:"hello")
var s = c.stringProperty
s.removeLast()
print(s) // hell
print(c.stringProperty) // hello
If you want to see the class-as-reference in action, make two of the same instance and do something to one of those:
class C {
var stringProperty : String
init(string:String) {
self.stringProperty = string
}
}
let c = C(string:"hello")
let d = c
c.stringProperty = "goodbye"
print(d.stringProperty) // goodbye

Input parameter to closure in Swift with brackets

I am going through the following tutorial on RxSwift:
http://adamborek.com/thinking-rxswift/
and having trouble understanding the following pattern:
searchBar.rx.text.orEmpty
------------> .flatMap { [spotifyClient] query in
return spotifyClient.rx.search(query: query)
}.map { tracks in
return tracks.map(TrackRenderable.init)
}
This square brackets input parameter: [spotifyClient] query seems very weird for me. I looked over official Apple documentation for closures and functions and I can not see any examples of such input parameters. In Objective C this would not bother me much, but it is Swift. Could anyone explain, what this parameter means here?
You will need to understand the variable capturing of closure idea.
Consider this example:
struct Calculator {
var a: Int
var b: Int
var sum: Int {
return a + b
}
}
Then you use this as:
let calculator = Calculator(a: 3, b: 5)
// You define a closure where you will use this calculator instance
let closure = {
// closure captures the variables that are declared prior to the declaration of the closure.
// your calculator instance is being captured here
// it's default variable capture
print("The result is \(calculator.sum)")
}
closure() // Prints "The result is 8"
Till now, everything is okay. You get what's expected.
Now consider you declare the calculator instance as var because in some point you need to mutate it's state. This is the case where complexity arises. Look:
var calculator = Calculator(a: 3, b: 5)
let closure = {
print("The result is \(calculator.sum)")
}
// You change the state of your calculator instance anytime before the closure gets executed
calculator.b = 20
// When the closure actually executes, you will be affected by any changes outside the closure
closure() // Prints "The result is 23"
So, the default variable capture isn't really helping you, instead it's creating problem in your case.
If you want to prevent this behaviour and print 8 even if the properties change after their capturing inside the closure, we can explicitly capture the variable with a capture list like this:
// [calculator] is your capture list
let closure = { [calculator] in
print("The result is \(calculator.sum)")
}
// change anything with calculator instance
calculator.b = 20
// execute the closure
closure() // Prints "The result is 8"
Capture List keeps immutable copy of the variable(s). Thanks to this copy, further changes to calculator, outside the closure, will not affect the closure.
You can capture multiple variables at once, hence it's called Capture List. Example:
let closure = { [variable1, variable2, variable3] in
print(variable1)
print(variable2)
print(variable3)
}
I recommend you read this article Capturing Values In Swift Closures.
Now, in your case spotifyClient is an instance of a class that may be responsible to make API calls. This instance may need some changes for calling different APIs. So, to prevent the affect of any changes to spotifyClient outside this closure you capture this instance in a Capture List.
Capture List vs. Parameter List:
You are confusing the parameter list with the capture list. The generic syntax is:
{ [capture list] (parameter list) in
...
...
}
Now take a look at the modified version of the above example:
let closure: (String)-> Void = { [calculator] stringParameter in // When using single parameter, you can always omit the () parentheses
print("\(stringParameter). The result is \(calculator.sum)")
}
// change anything with calculator instance
calculator.b = 20
// execute the closure
closure("Hey") // Prints "Hey. The result is 8"

Initializing class constants in Swift

I was trying to do something like this (it is a contrived example for demonstration purposes only):
class Test {
let hello = "hello"
let world = "world"
let phrase: String {
return self.hello + self.world
}
}
but you can't use let for computed properties in Swift. Is there a way to do this without having to write an init() method? Thanks!
The reason let doesn't work on a read-only calculated property is because it's used to state that the property's actual value will never change after being set – not that the property is read-only. As the Apple docs say (emphasis mine):
You must declare computed properties — including read-only computed
properties — as variable properties with the var keyword, because their
value is not fixed. The let keyword is only used for constant
properties, to indicate that their values cannot be changed once they
are set as part of instance initialization.
You therefore need to use var in order to reflect the fact that a calculated property's value could change at any time, as you're creating it on the fly when accessing it. Although in your code, this can't happen – as your hello and world properties are let constants themselves. However, Swift is unable to infer this, so you still have to use var.
For example:
class Test {
let hello = "hello"
let world = "world"
var phrase: String {
return self.hello + self.world
}
}
(This doesn't change the readability of the property – as because you haven't provided it with a setter, it's still read-only)
However in your case, you might want to consider using a lazy property instead, as your hello and world properties are constants. A lazy property is created when it's first accessed, and keeps its value for the rest of its lifetime – meaning you won't have to keep on concatenating two constants together every time you access it.
For example:
class Test {
let hello = "hello"
let world = "world"
lazy var phrase: String = {
return self.hello + self.world
}()
}
Another characteristic of let properties is that their value should always be known before initialisation. Because the value of a lazy property might not be known before then, you also need to define it as a var.
If you're still adamant on wanting a let property for this, then as far as I can see, you have two options.
The first is the neatest (although you've said you don't want to do it) – you can assign your phrase property in the initialiser. As long as you do this before the super.init call, you don't have to deal with optionals. For example:
class Test {
let hello = "hello"
let world = "world"
let phrase: String
init() {
phrase = hello+world
}
}
You simply cannot do it inline, as self at that scope refers to the static class, not an instance of the class. Therefore you cannot access the instance members, and have to use init() or a lazy/calculated property.
The second option is pretty hacky – you can mirror your hello and world properties at class level, so you can therefore access them inline in your phrase declaration. For example:
class Test {
static let hello = "hello"
static let world = "world"
// for some reason, Swift has trouble inferring the type
// of the static mirrored versions of these properties
let hello:String = Test.hello
let world:String = Test.world
let phrase = hello+world
}
If you don't actually need your hello or world properties as instance properties, then you can just make them static – which will solve your problem.
Yes to make it work as computed properties, replace let to var.
Like,
class Test {
let hello = "hello"
let world = "world"
var phrase: String {
return self.hello + self.world
}
}
This way you can use it without init()

Why does Swift BooleanLiteralConvertible require a boolean literal?

I am trying to add BooleanLiteralConvertible support to my class so I can instantiate it with a boolean. The thing that's throwing me for a loop is the distinction between a boolean value and a boolean literal.
For example, after adding the protocol I attempted this:
func setSelected(value: Bool) {
var node: MyClass = value
}
But Swift complained that it cannot convert Bool to MyClass. It took me a while to realize it has to be a boolean literal. Oddly enough the following works fine:
func setSelected(value: Bool) {
var node: MyClass = value ? true : false
}
…which seems just absolutely silly to me. Is there a legitimate reason for this seemingly very bizarre requirement?
Types conforming to BooleanLiteralConvertible can be initialized with the Boolean literals true and false, e.g.
let mc : MyClass = true
This has nothing to do with initializing the type with a Boolean value:
let value : Bool = // ... some boolean value
let mc : MyClass = value // error: cannot convert value of type 'Bool' to specified type 'MyClass'
and there is – as far as I know – no way to make such an implicit
conversion work. You would have to write a custom init method
init(bool : Bool) {
// ...
}
and initialize the object as
let value : Bool = // ... some boolean value
let mc = MyClass(bool: value)
I like the question. Only the Swift team could definitively answer, but I can speculate as to why: converting a typed value into a variable of a different type without an explicit conversion or cast is very easy to confuse with a programmer error, and in many cases is something the compiler should warn about.
Example (and assume that Person is also a StringLiteralConvertible that can be initialized with a string variable as well as a literal as you pose in your question):
struct Person {
private static var idCounter = 1
var name:String
let id:Int
init(withName name:String) {
Person.idCounter += 1
self.name = name
self.id = Person.idCounter
}
}
var person = Person(withName:"Mary")
let name = "John"
person = name
The above code looks suspiciously like a mistake, where the programmer is assigning a value of the wrong type (String) to a variable of type Person. It may in fact be a mistake. Maybe the programmer only meant to change the name of the person (person.name = name) without creating a new Person with a new unique id. Or maybe the programmer intended to assign some other value to person but made a typo or code completion error. Hard to tell without either being the original programmer, or carefully studying all the context to see whether this conversion makes sense. And it gets harder the further the assignment is from the place where the variables are originally initialized Should the compiler warn here that a value of type String is being assigned to a variable of type Person?
The example would be far more clear, and more in line with Swift conventions as:
var person = Person(withName:"Mary")
let name = "John"
person = Person(withName:name)
The above version is completely unambiguous, both to the compiler and to any other programmers who read this later.

Are computed properties evaluated every time they are accessed?

I have two questions about computed properties in Swift.
Are computed properties evaluated every time they are accessed? Or they are stored somewhere for future access?
What kind of property is this, since I couldn't google it out:
let navigationController: UINavigationController = {
var navigator = UINavigationController()
navigator.navigationBar.translucent = false
return navigator
}()
Is this also evaluated every time it is accessed?
That is NOT a computed property.
let navigationController: UINavigationController = {
var navigator = UINavigationController()
navigator.navigationBar.translucent = false
return navigator
}()
It is just a stored property populated with the result of the value returned by this block of code.
var navigator = UINavigationController()
navigator.navigationBar.translucent = false
return navigator
The block is executed when the instance of the class is instantiated. Only once.
So writing this
struct Person {
let name: String = {
let name = "Bob"
return name
}() // <- look at these
}
is equivalent to this
struct Person {
let name: String
init() {
self.name = "Bob"
}
}
IMHO the first approach is better because:
it does allow you to declared and populate a property in the same "space"
it's more clear
does prevent duplication of code if you have multiple initializers
Note #1: Storing a closure inside a property
As dfri noted in the comment below, the block of code does end with (). It means that the code is evaluated and the result assigned to the property.
On the other hand, if we remove the () at the end of the block, we get something different, infact the block is not evaluated.
In this case Swift tries to assign a stored closure to the property. This will produce a compile error since the property has this type UINavigationController.
With the correct syntax we can put a closure inside a property.
struct Person {
let sayHello: ()->() = { print("Hello") }
}
Now we have a sayHello property which contains a closure. The closure receives 0 parameters and does return Void.
let bob = Person()
bob.sayHello // this does NOT execute the code inside closure
bob.sayHello() // this does execute the code in the closure and does print the message
Note #2: let's talk about Computed Properties
So we made clear that code in this question is not a Computed Property.
However, as EmilioPelaez noted in another comment below, we should also state that a Computed Property is evaluated each time it is accessed.
In the example below I created a Computed Property age. As you can see each time I invoke it, the code in the block gets executed as well.
Example of a Computed Property (age)