Simple swift delegate in swift playground - swift

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.

Related

Passing self into initializer as delegate within own initializer

might be a silly question, but I'm trying to understand better why I can't do this. I recall this working in Swift 5.6.1, but I recently updated to Swift 5.7.2.
Before asking, I want to note that I did see this question: Swift passing self as argument in class init, but it didn't quite answer my question. Or maybe I just want to see if these are the only solutions...
I have a couple of classes that's something like this.
class Bar {
weak var delegate: FooDelegate?
init(delegate: FooDelegate) {
self.delegate = delegate
}
}
class Foo: FooDelegate {
var bar: Bar
init() {
self.bar = Bar(delegate: self)
}
}
Before I updated, I don't remember this throwing any errors. Now I'm getting the error
Variable 'self.bar' used before being initialized.
Is there a way to set this up so that I'm passing the delegate correctly?
Thanks all!
You can solve this by breaking it up in two steps, create the Bar object and then set delegate
init() {
bar = Bar()
bar.delegate = self
}
Of course this requires a new init for the Bar class

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.

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

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
}
}

Confusions about weak delegate in swift

Let us suppose we have a protocol
protocol MyProtocol {
fun someFunc()
}
class AClass {
var delegate: MyProtocol?
}
AClass doesn't care if the delegate is a class or struct. What I want is sometimes the delegate can be a class and sometimes it can be assigned to a struct.
My question is if I should make the delegate to be "weak".
If so, I have to make MyProtocol be a "class Protocol" so that the delegate has to be a class only.
If not, when I assign the delegate to class, how can I avoid retain cycle?
Thanks for any hint!
should make the delegate to be "weak"
The answer is that if MyProtocol is not restricted to classes, you cannot make it weak, the compiler won't let you.
The reason for the above is that structs are value types. There isn't a reference that can be strong or weak, because logically the entire struct is copied in when you assign the delegate.
how can I avoid retain cycle?
This means that you have got to be careful that your delegate contains no strong references back to the instance of the class. So, for instance
struct ConcreteDelegate: MyProtocol
{
fun someFunc() {}
var instance: AClass
init()
{
instance = AClass()
instance.delegate = self
}
}
Causes a reference cycle. It can be broken by declaring instance as
weak var instance: AClass!
Alternatively, and a better solution (IMO), your protocol functions can pass the instance as a parameter so the delegate never needs to store a reference to the instance.
protocol MyProtocol {
func someFunc(caller: AClass)
}
You'll see the above approach adopted in Cocoa in lots of places, for example with the table view data source protocol.
I think that you forgot that struct are not reference type but a value type. Meaning that a class has a reference in the memory heap but structures, enums haven't. By remembering this fact, there is no meaning to put weak if the delegate of your protocol is a struct because it can't cause a retain cycle.
You need to worry about retain cycles when you use only classes. If your delegate of your protocol is a class put weak if you think that your class have a reference of your protocol AND your protocol can have a reference of your class put weak that's the retain cycle.
If you want to check it put deinit functions when you are testing and see if your class is correctly deinit and not kept in memory.
It's basically what I know hope it will help you.

Swift Delegation

I'm having trouble wrapping my head around delegation in Swift. After reading some guides, I was able to set it up delegation between two ViewControllers, but I'm not understanding how it works. In my first view controller, I have a a label that displays what has been entered in the second view controller which contains a text field and a button (that returns to the first view controller). Here is the code for the first view controller:
#IBOutlet weak var labelText: UILabel!
func userDidEnterInformation(info: String) {
labelText.text = info;
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "transition"){
let secondVC: SecondViewController = segue.destinationViewController as! SecondViewController;
secondVC.delegate = self;
}
}
Here's the code for the second view controller:
protocol DataEnteredDelegate{
func userDidEnterInformation(info: String);
}
#IBOutlet weak var userText: UITextField!
var delegate: DataEnteredDelegate? = nil;
#IBAction func buttonPressed(sender: AnyObject) {
let information = userText.text!;
delegate!.userDidEnterInformation(information);
self.navigationController?.popToRootViewControllerAnimated(true);
}
My understanding is that in the text inside the text field gets stored in the information constant, then the userDidEnterInformation method from the protocol is called, with the method being defined inside the first view controller. This method then changes the label inside the first view controller. The thing is, I'm not sure what is happening in the prepareForSegue function. Specifically, I'm not sure what's the purpose of secondVC.delegate = self.
I would appreciate any sort of clarity on delegation.
The diagram is simple but can help you understand what's going on.
FirstViewController must conform to the DataEnteredDelegate protocol you have defined (see Sumit's answer). When using secondVC.delegate = self, you are saying that for the segue transition with the destination being a SecondViewController, the attribute delegate of that SecondViewController instance will be set to this instance of FirstViewController, thus delegating things from SecondViewController to your FirstViewController as made possible by the DataEnteredDelegate protocol.
The protocol you created in second viewcontroller is an Interface. You must implement your first view controller with the DataEnteredDelegate protocol.
class FirstViewController:UIViewController, DataEnteredDelegate{
func userDidEnterInformation(info: String) {
//stub
}
}
If the delegate of the second VC is not set in prepareForSegue() it remains nil. The second VC is then unable to call the first VC.
On a side note, if the delegate is nil your code will crash because delegate! is trying to unwrap an optional binding with the value of nil. It's better to first unwrap the delegate variable:
if let handler = delegate {
handler.userDidEnterInformation(information)
}
Alternatively, you could use Swift's Optional Chaining, calling userDidEnterInformation only if delegate is not nil.
delegate?.userDidEnterInformation(information);
In addition it is recommended to declare the delegate weak, to prevent retain cycles:
weak var delegate: DataEnteredDelegate?
Delegates and Protocols
Do not try to figure out how the dictionary definition of “delegate” fits with the concept of delegation in Swift. It doesn't.
Delegation in Swift is an agreement between two players—a sensing object and a requesting object. The “delegate” is the “requesting object.” Just think “asker” or “requester” every time you see “delegate” and it will make a lot more sense. Here is their agreement...
The Sensing Object (Second View Controller):
I have data from some event that took place. I will publish instructions (a protocol) on how you may access that data. If you want it, you must do three things.
You must declare in your class type that your class abides by my protocol.
You must write the functions that I describe in my protocol. I don't care what those functions do but the function type must match what I publish.
In YOUR code, you must set MY “delegate” (think “asker”) property to point to you. {secondVC.delegate = self} That way I can call one of YOUR functions to deliver the data.
After that, when I get some data, I will call one of the functions in your object that I told you to write. My call to your function will contain the data you are looking for as one of the arguments. {delegate!.userDidEnterInformation(information)} Note: delegate! (asker!) is YOU.
The Delegate (Requesting) Object (First View Controller):
O.K. You've got a deal.