Why doesn't referencing outside of a function create retain cycle? - ios

Sorry, but I know this is a really dumb question, and I already kind of 'know' the answer, but I need someone to clearly explain to me WHY the answer is what it is.
Lately, I've become a bit obsessed/paranoid about retain cycles and memory leaks in my code, after going through some nightmarish debugging with various memory issues, so in the future I want to nip them in the bud. But after reading and learning a lot about ARC and retain cycles in Swift, although it makes sense, I still don't really have enough of an "intuitive" or natural feel for it, to feel confident that I could spot one, or the lack of one, as I'm coding. So I'm starting to become a little paranoid that I'm creating retain cycles with even basic stuff without realizing it.
So, with that in mind, why doesn't any ordinary function that uses a variable declared outside of it create a retain cycle? For example:
class someClass {
let a = "I'm letter a"
let moreLetters = addLetters()
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
}
In this case, self.moreLetters references the function addLetters, and then the constant self.a is references from within the function addLetters. So would this create a retain cycle if I don't capture weak/unowned self? It seems absurd to me that something this simple would cause a problem...or is it? What about in a nested function, like this:
func someFunction () -> String {
let a = "I'm letter a"
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
let moreLetters = addLetters()
return moreLetters
}
Would that also create a retain cycle? (Yeah I know this is a convoluted way of performing a simple task; I'm just using this code as an example to make my point).
Have I become super-paranoid and am severely overthinking things?

First, you need to understand how a basic retain cycle is formed. A retain cycle is formed when an object A refers to an object B strongly and at the same time. object B refers to object A strongly as well.
Let's look at your first bit of code.
class someClass {
let a = "I'm letter a"
let moreLetters = addLetters()
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
}
Actually, a class by itself can never create retain cycles, so let's add some code to create an object:
var obj = someClass()
First, a is initialized to "I'm letter a". After that, moreLetters is initialized by calling the method addLetters. After the method returns, moreLetters is initialized to "I'm letter abcdefg". So far so good.
Now we set obj to nil:
obj = nil
If a retain cycle were formed, obj would not be deinitialized. However, in actuality, obj is deinitialized because nothing holds a strong reference to obj!
"Wait a minute!" you say, "But the method addLetters still refers to someClass because it has a in it!" Well, in fact, addLetters has already returned! Therefore, everything in it doesn't matter anymore! In addition, addLetters belongs to obj, which you have already set to nil!
Now let's look at your second code:
func someFunction () -> String {
let a = "I'm letter a"
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
let moreLetters = addLetters()
return moreLetters
}
A retain cycle does not form because there isn't a even a reference type! There is no objects to be created. All you did in the second code is playing with strings, which are value types. Even if there were a class, a retain cycle would not form because as I said, when you set obj to nil, all the methods in it "disappear" because methods belong to objects.
What about those closures where I must write [weak self] or a retain cycle forms?
Those closures are escaping closures. Normal closures are deinitialized after they return. However, escaping closures are retained by some object so they are not deinitialized immediately. For more info, see Escaping Closures in Swift

Related

Is there a tangible benefit to using "self" outside of closures?

I have noticed a few people in the industry will use the self keyword even when not explicitly required (i.e. outside of closures).
Example:
import UIKit
import MapView
import CoreLocation
class viewController: UIViewController, MKMapViewDelegate, CLLocationDelegate {
let mapView = MKMapView()
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
self.mapView.showsUserLocation = true
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
}
Is there a tangible benefit to this, runtime-wise? Or is this purely a stylistic choice?
There is.
In the examples you provided it makes no difference, it's purely a style choice. Some people might like it since it explicitly tells you you're modifying self, personally I think it looks cleaner without.
Where it matters is when you have a local variable with the same name. Let's say you have a class that has a var count: Int property. Then, in one of your methods, you declare a new variable with the same name.
The local variable will be used whenever you type count, so if you want to modify or read the object's variable, you'll need to use self.
Some examples where it makes a difference:
guard let count = calculateCount() as? Int else { return }
self.count = count
init(count: Int) {
self.count = count
}
func updateCount(_ count: Int) {
self.count = count
}
Yes there are some benefits.
Using keywords like self or init or Swift prevent Xcode from ambiguity of overloads
make it compile faster
brings autocomplete suggestions faster;
that's it! Even though you use them explicitly, the compiler removes them from compiled code for compressing reasons. But it is not preferred! It's all about the scope of variables and functions.
Take a look at this example:
let name = "global variable"
class MyClass {
let name = "object variable"
func testScopes() {
let name = "function local variable"
print(name) //prints: function local variable
print(self.name) //prints: object variable
print(MyProject.name) // prints: global variable
}
}
let myObject = MyClass()
myObject.testScopes()
Here are three variables with three different valid scopes. You can refer to each one you need differently.
Swift community suggested:
For conciseness, avoid using self since Swift does not require it to access an object's properties or invoke its methods.
Use self only when required by the compiler (in #escaping closures, or
in initializers to disambiguate properties from arguments). In other
words, if it compiles without self then omit it.
But it's ultimately up to you and your company's code style guide.
Aside from the practical reason where it is required when there is a local variable or parameter with the same name, I do it because for a couple of reasons:
Habit from Objective-C
It explicitly shows me (and anyone else that looks at my code) that I am accessing a property, not a local variable; ie. this action may have consequences (or be influenced by events) outside of the current context.
My personal convention is to only use self in closures. For a few reasons.
It tells me, as a developer, that we are explicitly in a closure, because self is required.
self is very important in closures, and lets us know of possible retain cycles and memory leaks when we capture self strongly.
It is redundant when not in a closure, when there are not multiple variables of the same name inside the same or different scopes.
One thing I will say, when coding, using self. is a quick way to see what's available in the class or struct we are in. However, I will remove it after I find what I need.

Swift, why don't class methods need closure lists

If functions are essentially closures. Why don't methods of a class need closure lists when referencing self or another instance property within the closure.
Is there a [unowned self] behind the scenes? For example:
class MyClass{
func myFunc(){
self.otherFunc()
}
func otherFunc(){
print()
}
}
Wouldn't there be a reference cycle within myFunc? Ie, the closure is pointing to self, and the instance is pointing to the function. Neither could be deallocated.
"If functions are essentially closures." This isn't true. Functions (and methods) are not the same thing as closures. Functions have all their free variables unbound. Closures have bound some or all of their free variables (closed over them, which is where the name "closure" comes from).
A "free variable" is any variable defined outside the scope of the function (including its formal parameters). The top-level function func f(x: Int) has one free variable; when you call it, you must pass a parameter. A closure like { f(1) } has no free variables. When you call it, you do not pass any parameters.
A method, like a function, does not capture anything. It is passed all of its free variables when it is executed. For example, when you make the call object.doThis(), this is the same as calling Type.doThis(object)().
class X {
func doThis() {}
}
let x = X()
x.doThis()
X.doThis(x)() // Same thing
X.doThis(x) is a function that returns a function. There's no magic here. All the free variables are provided during the call. Nothing is captured. (The "free variable" in the case you describe is self, but that doesn't change anything. self is not special, except that it gets a little syntactic sugar around it.)
This is different than a closure:
let c = { x.doThis() }
c()
When I call c(), how does it know the value of x? I may have returned c and x may be out of scope now. The system has to keep track of x (including making a strong reference so it doesn't deallocate), and it does that by capturing it, or "closing over x" which raises the possibility of retain loops. So in c, x is bound. It is not free. You can't pass it when you call c().
self is not special here. It's just another variable. [weak self] in closures isn't special either. You can write [weak x] just as well. The [...] syntax is just the capture list.
Closures may only cause reference cycles when the closure is kept alive. Consider this:
let foo = MyClass()
let bar: () -> () = { in
print(foo)
}
The bar closure holds a reference to foo, but that reference goes away once nothing references bar anymore. For instance:
func f(foo: MyClass) {
let bar: () -> () = { () in
print(foo)
}
}
This does not create a reference cycle, because when f returns, the closure in bar is destroyed. Similarly, when you call myFunc and otherFunc, you do need a strong reference to self (the compiler ensures that you have it), but as you no longer need it at the end of the function, no cycle is created.
In general, a closure will not systematically create a reference cycle, even if it is #escaping. Consider the case of Dispatch.async:
class MyClass {
func foo() {
DispatchQueue.main.async {
print(self)
}
}
}
This does not actually create a reference cycle, because even though the closure references self for a while, self does not reference the closure.
The dangerous case is this one:
class MyClass {
var closure: () -> ()
func f() {
self.closure = {
print(self)
}
}
}
This one actually creates a reference cycle: self.closure has a strong reference to self, and self has a strong reference to self.closure.

How memory leak will occur in swift?

I am new to ios development and i want to learn how memory leak will occur in swift or in Objective-C, can any one explain with small example?
Thanks
Small example:
class A {
var b: B!
deinit {
print("deinit of A")
}
}
class B {
var a: A!
deinit {
print("deinit of B")
}
}
do {
let a = A()
let b = B()
a.b = b
b.a = a
}
If you run this code (maybe in Playground), it will print nothing. It means that deinit never called for both objects and they just leaked.
But if you declare one of the properties as weak:
class A {
weak var b: B!
deinit {
print("deinit of A")
}
}
Then deinit will be called and you'll see messages in console.
Edit: add example with closures
Consider this example:
class C {
var f: (Void -> Void)!
deinit {
print("deinit for C")
}
}
do {
let c = C()
c.f = {
print(c)
}
}
c captures f, f captures c. So we got memory leak.
To deal with leaks in closures, you have 2 options – declare that captured object is weak or unowned. Like this:
do {
let c = C()
c.f = { [weak c] in
print(c)
}
}
Basically, you would use weak if it's possible for object to get out of existence and become nil when closure is called; but if you are sure object will still exist at this time, use unowned instead.
After I declare c as weak inside a closure, "deinit for C" is printed – means that everything deallocated successfully.
What this all means for you, the developer?
Almost all the time you don't have to worry about the memory management. It's done for you, automatically. Objects just exist when you need them and vanish when you don't. But there are 2 very common cases when you need to be careful and think about memory.
Delegation. It's common pattern in Cocoa and it could create retain cycles if done wrong. Always declare your delegate as weak unless you have a good reason not to.
Closures. Closure capture references for objects in surrounding scope and does it automatically, without notice. When implementing closure, chech if it will create retain cycle. If yes, declare problem variables as weak or unowned.
For further info I suggest you read official Apple Swift book which can be found in iBooks or here as a website.

How do I deinit a lazy var in Swift?

I've been reading a bunch about Swift initilization lately, and one solution that appears to have some merit is utilizing lazy variables to avoid the doom and gloom of optionals, especially when extending UIViewControllers or other classes along those lines. With that in mind, I've got code that looks like this:
final class MyViewController : UIViewController {
lazy private var myOtherViewController: MyOtherViewController = MyOtherViewController()
deinit {
// myOtherViewController = nil // Can't do this. Expression resolves to an unused l-value
}
override func viewDidLoad() {
super.viewDidLoad()
myOtherViewController.foo();
}
}
// elsewhere...
final class MyOtherViewController : UIViewController {
deinit {
print("Deinit!")
}
}
In the example here it appears that MyOtherViewController's deinit method never seems to fire, but I also have no way to make myOtherViewController deinit. Setting it to nil isn't allowed. Is there something I'm missing that forces MyOtherViewController to retain? Or have I just been impatient with the garbage collector?
To do this, your lazy variable would have to be declared as optional even though you intend to initialize it as soon as the value is needed (i.e. lazily).
class MyObject {
func run() {
print( "MyObject :: run()" )
}
init() {
print( "MyObject :: init" )
}
deinit {
print( "MyObject :: deinit" )
}
}
class MyContext {
lazy var myObject:MyObject? = MyObject()
}
let myContext = MyContext()
myContext.myObject?.run() //< "MyObject :: init"
myContext.myObject = nil //< "MyObject :: deinit"
Also, I disagree with the notion of the "doom and gloom of optionals"—one only need know which of the many available techniques is most convenient and practical way handle them, and how to avoid allowing too many permutations of state to exist among combinations of optional and non-optional values in a given context. Furthermore, an optional is actually exactly what you want here because you intend to nullify it. Employing an optional doesn't mean that a value will be nil for now until you initialize it, but rather that it may be nil at any point in its life, even if it is defined at declaration or any other time beforehand. If avoiding optionals is truly that high of a priority for you, you'd have to take a step back and re-evaluate your architecture to create a situation where you no longer have the need to de-initialize the value.
I agree with Grimxn, that this pattern of setting that property to nil (and thus needing to make it optional in order to be able to do that) is unnecessary. When MyViewController is deallocated, it automatically releases its strong reference to MyOtherViewController.
The only time you need to manually nil this property is when you have to resolve a strong reference cycle (and if you had such cycle, you can't resolve that in deinit, but rather viewDidDisappear or something like that).
If you're not seeing this other view controller get deallocated, then I'd suggest finding the source of the strong reference cycle and resolving that.

Passing argument to instance method to avoid retain cycle?

I've recently started profiling one of my iOS app written in swift and realized how annoying ARC is compared to other more adopted GC like Mark-And-Sweep.
One of the most prevalent causes for Strong Reference Cycle was retaining instance property within a closure that is passed to another object retained, again, by the class.
For example,
class MyClass {
private let text = "hello world"
private let anotherClass = AnotherClass()
init() {
addText()
}
private func addText() {
anotherClass.addText { return self.text }
}
}
Retain cycle like above can be avoided by passing argument to the method instead of accessing self directly.
class MyClass {
private let text = "hello world"
private let anotherClass = AnotherClass()
init() {
addText(text)
}
private func addText(text:String) {
anotherClass.addText { return text }
}
}
Is the second approach considered a good practice?
FYI, I'm aware that retain cycle like above can be broken using capture list. I'm just curious as to patterns that are more resilient to memory leaks.
Since the property let text ... is a constant, I think that the second approach is fine and can be considered a best practice.
But if you use the same approach with a var text ... property, then the behaviour changes:
in the first approach an execution of the closure will use the current value of text,
while in the second approach an execution of the closure will always use the original value of text.

Resources