Xcode 10 - Instance will be immediately deallocated because property is 'weak' - swift

I recently downloaded Xcode 10 and I noticed an apparent bug when using weak or unowned variables. I managed to create a simple example that showcases the problem so that people can recreate it.
class MainClass {
weak var weakClass: SomeClass!
init() {
// WARNING: Instance will be immediately deallocated because property 'weakClass' is 'weak'
self.weakClass = SomeClass()
}
}
class SomeClass {}
As the error says, weakClass immediately deallocates once MainClass is initialized and is always nil.
I have opened up the same playground with Xcode 9.3 and I can confirm that the code works fine with no errors or warnings
Is this a bug in Xcode 10 or am I not getting something. If it is, is there any workarounds?
EDIT: Original Example
class LoginCoordinator {
var viewModel: LoginViewModel?
var viewController: LoginViewController?
init() {
viewModel = LoginViewModel()
viewModel?.coordinator = self
viewController = LoginViewController(viewModel: viewModel!)
}
}
class LoginViewModel: ViewModelDelegate {
weak var coordinator: LoginCoordinator?
}
coordinator is always nil in LoginViewModel
AppDelegate.swift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func setupView() {
let coordinator = LoginCoordinator()
let navigationController = UINavigationController(rootViewController: coordinator.create)
navigationController.isNavigationBarHidden = true
navigationController.navigationBar.isTranslucent = false
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
window?.layer.cornerRadius = 6
window?.layer.masksToBounds = true
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
setupView()
return true
}

To understand this you must know the concept of ARC. ARC concept is automatic reference count means ARC will keep something in memory, as long as an allocated memory is strongly referenced by some variable. If it(ARC) found some allocated memory doesn't have any strong reference it will dealloc it. So the warning weakClass immediately deallocates once MainClass is initialized and is always nil. Because it doesn't have any strong reference.Please comment any doubt.
One example below for retain cycle creation:
class A {
var classBObject: B?
init() {
classBObject = B()
classBObject.classAObject = self // Creates a retain cycle
}
}
class B {
var classAObject: A? // Strong(by default all are strong) variable create retain cycle
}
So, in class B if we take weak var classAObject retain cycle will not happen.

This is the purpose of weak. Swift uses reference count to manage memory. A strong pointer increases the reference count of the pointed object by 1, a weak pointer does not increase reference count. An object with 0 reference count will be deallocated.
Your instance of SomeClass only pointed by a weak pointer, so its reference count is 0. As a result it is deallocated immediately.
Weak is useful to avoid retain cycles. For example, in escaping closure and in delegation design pattern.

The question is, "is that reference strongly referenced elsewhere? If so, it will not be deallocated."
I propose that Apple's warning message is misleading. I think that it should state that it will be deallocated immediately when its containing object is deallocated or when other strong references to it are deallocated.
Here's why.
We have this warning on an instance in a view controller and the weak var is not deallocated immediately. The view controller appears, the weak var is instantiated, we wait, click a button that hits a breakpoint and yup, the weak var is still not nil. Yet when the view controller disappears and is deallocated, the weak var is deallocated immediately.
But why? Well, by the time we come to the part of code that has a weak reference to the variable, other code already has caused this variable to have retain count of 3. This means that even though it's weak, it can't be immediately dismissed.
You can check this with po myObject.retainCount. It's not guaranteed to be accurate, but it will give you an idea. If the object's retainCount > 1 and it's strongly linked somewhere else, (please put a comment in your code to indicate where it is strongly referenced), weak will work. To avoid a compiler warning, don't reference the object directly, but the strong reference in the other object.
So, I think that Apple needs to reword this warning because it's surely misleading.

Further, given that a Swift property doesn’t have a corresponding instance variable, it makes sense that when you give a value to a weak property, it is immediately deallocated. I run into a similar problem here where my SceneDelegate asks for the rootViewController of this AppCoordinator :
class AppCoordinator {
private weak var navigationController : UINavigationController!
var rootViewController : UIViewController {
navigationController = UINavigationController()
return navigationController // <-- here the app crashes: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
}
}

Related

Object created within a function disappears despite being passed further

Coming from a different language I got surprised by a silent bug where an object which is passed to a method as a callback suddenly is never called. The reference to a callback is somehow lost.
Minimal (not runnable) example of a problem:
class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
let delegate = Foo() //nonsensical in this case, in normal case diff. object will be used
out.startRecording(to: /*...*/, recordingDelegate: delegate)
//Result: delegate methods are never called
}
}
Minimal (not runnable) example of a "solution":
class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
out.startRecording(to: /*...*/, recordingDelegate: self)
//Result: delegate methods are called when approperiate
}
}
I'm puzzled...
Why does that happen?
How to prevent such situation?
Is such silent failure by design?
This question stems from the AVCaptureMovieFileOutput never calls delegate on screen recording
Most delgates are weak so that they do not create a retain cycle, see Automatic Reference Counting (ARC). Most of this is assuming that the delegate storage for what you are using is weak.
In your first example the only strong reference to the object is held by the function bar, as the delegate is a weak reference. Once the function ends, the only remaining strong reference is gone and the object is free to be deleted.
class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
let delegate = Foo() //object created, and strong reference stored in variable called delegate
out.startRecording(to: /*...*/, recordingDelegate: delegate) // object passed in and likely stored in a weak variable inside of the `out` object. This means it will not keep a strong reference to your Foo object.
//Result: delegate methods are never called
}// local variable called delegate goes out of scope, strong reference to your Foo object goes away, there are no more strong references, can be deleted.
}
In the second example when using self as the delegate, self is likely sticking around after the end of the bar function, so the delegate remains.
class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
func bar() {
let out = AVCaptureMovieFileOutput()
out.startRecording(to: /*...*/, recordingDelegate: self) // pass `self`, which presumably has something else referencing it with a strong reference, so it stays alive
//Result: delegate methods are called when approperiate
} // `self` still has strong references to it (somewhere else) keeping it alive after the function call, so the weak reference that is being used to call the delegate methods can still operate! Yay!
}
Hopefully that answers the "why."
As for prevention, you need to make sure you keep a strong reference to any delegates (or weak variables) that you want to stay alive.
This behavior is by design as it is used to prevent retain cycles and memory leaks. When designing your own classes with delegates it is up to you to use weak appropriately to prevent retain cycles when necessary.
As for the silence of the failure, there are many cases where the delegates are optional, and it is not considered a failure for the delegate to be nil and for the delegate functions to not get called. Many times the functions are called like delegate?.delegateMethod() intentionally so that the function will be called if you want to have a delegate and it won't cause a problem if you don't want to have a delegate.

How to instantiate a weak delegate without triggering "Instance will be immediately deallocated because property 'tableViewDelegate' is 'weak'"

I am trying to separate out my tableView's datasource into a separate delegate object. Since that delegate needs to access the tableview at some point I need a reference to the delegating object in the delegate; and since both are classes I need to avoid strong reference cycles by making the delegate weak
To achieve this I tried the following code.
class MyViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
weak var tableViewDelegate: UITableViewDataSource?
override func viewDidLoad() {
super.viewDidLoad()
tableViewDelegate = TableViewDelegate() // throwing a warning
tableView.dataSource = tableViewDelegate
}
}
When I try to instantiate the delegate Xcode throws a warning: "Instance will be immediately deallocated because property 'tableViewDelegate' is 'weak'"
So to fix it I do the following:
class MyViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
weak var tableViewDelegate: UITableViewDataSource?
override func viewDidLoad() {
super.viewDidLoad()
let delegate = TableViewDelegate() // worried this creates a strong reference.
self.tableViewDelegate = delegate
tableView.dataSource = delegate
}
}
Please confirm if the following is true or not: by initialising the delegate in the viewDidLoad() method I am not in danger of creating a strong reference because the variable that holds that instance is deallocated as soon as we leave the scope of that method. Or to put it another way: the only time we need to worry about a variable (which points to a class) creating a strong reference is if the variable is initialised at the class level, and hence will live on as long as the class does.
Is that correct?
Please confirm if the following is true or not: by initialising the delegate in the viewDidLoad() method I am not in danger of creating a strong reference because the variable that holds that instance is deallocated as soon as we leave the scope of that method.
Correct. The strong reference goes away as soon as the scope in which the let is declared exits.
Unfortunately, that means your delegate will still be deallocated. All you have done is silence the warning.
Basically, you need to have a strong reference to the delegate somewhere or it will go away straight away. My feelings is that you should make the reference in MyViewController strong. There will be no strong reference cycle as long as your delegate does not contain a strong reference to the view controller. If you need a reference to MyViewController in the delegate, make that a weak one i.e. the view controller owns the delegate, not the delegate owns the view controller.
Response to the comment below:
almost all the tutorials I have found have the delegate property as weak, so it seems standard practice.
Yes it is fairly standard practice, there are exceptions including in Cocoa. However, it is standard practice to have a weak reference to the delegate in the delegating object. In your case, the delegating object is thew UITableView not the MyViewController. In your first example from the Internet, the FileImporter is analogous to the UITableView in your code. In the second example, the DetailViewController is the delegating object.
If you think about it, your TableViewDelegate is being used in place of making MyViewController conform to the protocol. It makes absolute sense that the MyViewController would own the delegate.
This is how I solved this problem:
let dataSource = MyDataSource()
lazy var viewModel : MyViewModel = {
let viewModel = MyViewModel(dataSource: dataSource)
return viewModel
}()
and then in viewDidLoad():
tableView.delegate = self
tableView.dataSource = dataSource
You can see the full demo project here

Why Swift disallows weak reference for non-optional type?

This is not pure curiosity, there is a feeling that I may misunderstand something about weak references in Swift.
Suppose I create a class from a View Controller and pass its reference to the initialiser:
class = MyClass(vc: self)
Since the storyboard and window already keep a reference to this View Controller, it seems logical for MyClass to have a weak reference to it (for the similar reason all references created within IB are weak by default):
class MyClass: NSObject {
private weak var viewController: UIViewController
init(vc: UIViewController) {
self.viewController = vc
super.init
}
func setViewController(_ vc: UIViewController) {
self.viewController = vc
}
...
}
However this code gives compilation error, as viewController variable isn't optional. So I had to add '!' to viewController declaration and remove the initialiser, leaving only setViewController which looks rather unnatural.
What is the reason behind disallowing non-optional weak data?
The very definition of a weak variable is that the variable does not increase the reference count of the object and, more importantly for your question, the variable's value will automatically be set to nil when the referenced object gets deallocated.
Since the variable must allow for a nil value, it must be optional. This is why non-optional weak variables are disallowed.
Do not declare viewController to be implicitly unwrapped (using !). Make it a proper optional (using ?).

Swift - Expecting a leak after strongly capturing self in closure

Can anyone please explain why this doesn't leak?
I'm capturing self within a closure so I would have two strong pointers pointing at each other, therefore, the deinit message shouldn't ever be called for the Person object.
First, this is my class Person:
class Person {
var name: String
init(name: String) { self.name = name }
deinit { print("\(name) is being deinitialized") }
}
And this is my ViewController's implementation:
class ViewController: UIViewController {
var john:Person?
func callClosureFunction( closure:(name:Bool) -> () ) {
closure(name: true)
}
override func viewDidLoad() {
super.viewDidLoad()
john = Person(name:"John")
self.callClosureFunction { (name) in
self.john?.name = "John Appleseed"
self.john = nil
// xcode prints - John Appleseed is being deinitialized
}
}
}
I was expecting to be able to fix the issue by doing:
self.callClosureFunction { [weak self] (name) in ...
But that wasn't even necessary. Why?
Since your view controller is not retaining the closure, there is no circular reference. If you wrote this:
class ViewController: UIViewController {
var john:Person?
var closure:(Bool)->()?
func callClosureFunction( closure:((name:Bool) -> ())? ) {
closure?(name: true)
}
override func viewDidLoad() {
super.viewDidLoad()
john = Person(name:"John")
closure = { (name) in
self.john?.name = "John Appleseed"
// Because this closure will never be released, the instance of Person will never deinit either
}
self.callClosureFunction(closure)
}
}
then the view controller would retain the closure and the closure would retain the view controller via its reference to self. Therefore, neither would be released, and if you don't explicitly set self.john = nil (which you did in your original example), then the Person instance would never get deninit called.
It's quite common to inappropriately use weak self in closures when not necessary (and this can actually lead to some obscure bugs). The key rule to remember is that weak references are not the default in general under ARC. Strong should be the default unless it would lead to a retain cycle, in which case weak should be used only to break that circular reference. Same for closures: strong self should be the default, unless the self in this case also has a strong reference to the closure itself.
You're capturing self which points to ViewController, but you're wondering about the Person instance.
Person is actually not circular referenced and therefore gets de-initalized and released just fine when you set it to nil at the end of your closure.
Implement deinit for ViewController and see how that works.
I'm capturing self within a closure so I would have two strong pointers pointing at each other, therefore, the deinit message shouldn't ever be called for the Person object.
No, you have one strong pointer, from the closure to self. There's no cyclic reference back from the closure to self. Thus, you have a directed acylic graph, which is no problem for ARC.
However, your experiment is flawed, from the get-go. Even if the closure was captured, the John Appleseed Person object would still deinit. This object's lifecycle is exclusively dependent on on the john reference from your ViewController. When you set that reference to nil, you're removing the last reference to the John Appleseed object, thus it's deinitialized.

Simple swift delegate in swift playground

I'm very new to Swift and programming in general, a bit of Fortran 77 way back, and more recently some simple programming of microcontrollers. I'm working through the basics and all was well until i came across something that i just can't quite get to grips with - delegates. All the online posts don't quite get the concept across, at least for me, so to give myself something that i can refer back to, i've set up a basic template shown below in playground. If i run the code below it works and prints "Something done" to the terminal, but if i make the protocol a "class" protocol ie "protocol SomeDelegate: class {" and make the "var delegate" a "weak var delegate" as recommended in various posts, it doesn't work - what am i doing wrong?
import UIKit
protocol SomeDelegate {
func DoSomething()
}
class MyViewcontroller: UIViewController, SomeDelegate {
func DoSomething() {
print("Something done")
}
}
class OtherClass {
var delegate: SomeDelegate?
func DoSomething() {
delegate?.DoSomething()
}
}
var myVar = OtherClass()
myVar.delegate = MyViewcontroller()
myVar.DoSomething()
It doesn't print because the delegate is nil right after you set it. The reason for this is simple: no instance owns it (the reference count is zero). No one owns delegate because you declared it a weak property of OtherClass. Try establishing an ownership, e.g.
var myVar = OtherClass()
let viewController = MyViewController()
myVar.delegate = viewController
Even though delegate is weak, it will now print Something done again.
Declaring delegates as weak makes sense because it prevents circular references causing delegate to never be release in memory – that's a whole different story though – check how reference counting works, then you will understand why this is a good practice.