This question already has answers here:
How can I make a weak protocol reference in 'pure' Swift (without #objc)
(8 answers)
Closed 7 years ago.
I have a UIViewController and in it a UIToolbar. They get instantiated from a storyboard.
I made a custom class for my UIToolbar. Based on some logic I do or do not show buttons on it.
The UIViewController needs to take action when some of the buttons are tapped.
For this I created a delegate protocol in the UIToolbar.
Currently, when I dismiss the view, it is kept in memory. Further investigation revealed my delegate created a retain cycle.
In Objective-C, we would simply define delegates as weak. However, I am using Swift, and it does not allow me to define delegate variable as weak:
weak var navigationDelegate: MainToolBarDelegate?
// 'weak' cannot be applied to non-class type 'MainToolBarDelegate'
When I dismiss the view controller, I set self.toolBar.navigationDelegate = nil and the memory gets cleared. But it feels wrong!
Why do I get the retain cycle and why can I not simply define the delegate as weak?
weak references only apply to classes, not structs or enums, which are value types. But protocols by default can apply to any of those types.
Define your MainToolBarDelegate as a class-only protocol:
protocol MainToolBarDelegate: AnyObject {
}
Then you'll be able to declare your delegate as weak.
Related
I'm following a tutorial on Swift and I noticed that the author uses var instead of let when declaring an #IBOutlet variable. So I became curious as to why I can't use let instead since an object's properties are still mutable even if the object is constant or is this not the case?
The error Xcode shows when using let is
#IBOutlet attribute requires property to be mutable
but I'm confused because questionLabel is a UILabel object and not necessarily a property of an object. Or is the questionLabel object a property of the current viewController?
import UIKit
class ViewController: UIViewController {
#IBOutlet let questionLabel: UILabel!
}
Thank you in advance if I'm over analyzing.
The #IBOulet marked properties are generally properties of a ViewController that are connected using the interface builder. The view you create in the interface builder has to actually connect the interface elements in it to the properties during your actual application runtime.
For that reason it firstly creates a new ViewController using some init without connecting any interface elements. They only get connected at a later stage. For the runtime to be able to hook the properties up to the view elements after the object creation has completed they cannot be constants, they have to be mutable. Because they do not have a value after the initializer has finished they have to be optionals. And to not make using the properties cumbersome afterwards they are implicitly unwrapped optionals, so that you do not have to write label!.property but label.property suffices.
That is why your code crashes as soon as you try to do something with an IBOutlet variable which you failed to connect and that is also the reason why you cannot use / change / manipulate those fields in the initializer.
Regarding your actual var / let confusion. Yes, the object itself that is referenced using let can be changed, e.g. the text of a UILabel BUT the actual object reference cannot be changed. That would mean that if you do not give the constant a specific value in the initializer it would forever remain nil.
For the simple reason that it is not assigned during initialization (in the initXXX methods) but later, when the view is being loaded.
The compiler actually cannot be even sure that the variable is ever assigned because the view loading is comletely dynamic.
In swift, all vars and lets can be thought of as properties.
A property is immutable (a constant) if it's declared with let. It's mutable (a variable) if it's declared using the var keyword. That is the defining difference between let and var.
Outlets must be mutable because their value does not get set until after the object is initialized. (The view controller gets initialized and it's outlets don't get loaded right away.)
You are right questionLabel is an object of type UILabel but used as a property of your class ViewController. That's why you have #IBOutlet attribute requires property to be mutable. If you use var you are saying that a property is mutable. If you use let you are saying that the property is immutable.
Try to create questionLabel without #IBOutletand see what's going on. Probably, you can put let in front.
First the ViewController is created, then the view tree is built. That means that when the ViewController finished it's init these views don't exist yet. They will be added just before viewDidLoad by parsing the XML-like data of the storyboard or XIB.
I know that it's Apple's default way of doing things but I would always write my outlets like:
#IBOutlet let questionLabel: UILabel?
For the simple reason it's absolutely not proven this label will really exist at run time. For example when reusing ViewControllers over multiple screens, changing the layout after setting the connections and so on this outlet might not be set. If you would use the questionLabel defined as UILabel! and it would be nil your application will crash. I don't think applications in production should ever crash over something silly like that.
For real application safety the only way to know really for sure it exists is to build your UI's in code. But the ease of use of Storyboards for one off screens is really tempting, I still use them a lot.
This question already has answers here:
Should IBOutlets be strong or weak under ARC?
(11 answers)
Closed 5 years ago.
Let's say I programmatically create a UILabel and then add it as a subview to the main view of my own UIViewController's subclass. I also want to store a reference to it at the class level of my view controller so I can use it later. Should I make it a weak reference? And more importantly why?
I see people do this all the time, and I don't understand it. I thought the point of a weak reference was to avoid a retain cycle, but there's no retain cycle here.
I certainly see this all over code where people use storyboards/nibs (which I've never tried to use so I understand at only a very basic level) but even there I don't understand why weak references would be appropriate. There's no retain cycle there either, right?
No, there's no reason for that to be a weak reference, you have correctly identified it as a one-way relationship. The Interface Builder code generator has, in the past, generated weak references by default (which may be why you have seen it a lot.) Apple's guidance on this has changed over time, you can read about it here:
Should IBOutlets be strong or weak under ARC?
I found something that I can't understand in UICollectionView header file. I found that the delegate has an assign property
#property (nonatomic, assign) id <UICollectionViewDelegate> delegate;
This question is only for my basic understanding as the rule says the delegate should have a weak property. And according to my personal knowledge, assign won't reference count the delegate object but it will surely still have a reference to a garbage value if the object is deallocated.
How can I understand this piece of code?
strong and weak were introduced alongside Automatic Reference Counting (ARC). UIKit moved to ARC with iOS 9, and if you look at the iOS 9 header (using Xcode 7) you will see that this property is now weak.
You are right: with the property as assign (which is equivalent to unsafe_unretained), if the delegate is deallocated while the collection view is alive, the collection view’s delegate property will point to where the deallocated object used to be and probably cause a crash when it is referenced. This is not usually a problem because the delegate is often the view controller owning the collection view so usually outlives the view. However, this is not a guarantee, which is why you should set assign delegates that point to you to nil in your dealloc.
Relevant Stack Overflow questions:
Objective-C ARC: strong vs retain and weak vs assign
Set delegates to nil under ARC?
ARC delegate memory management
This question already has answers here:
Differences between strong and weak in Objective-C
(9 answers)
Closed 10 years ago.
I see this example in the beginning of iOS development ,the chapter of picker view.
And i don't understand why it use a strong reference here.
#property (strong, nonatomic) IBOutlet UIDatePicker *datePicker;
We always use weak property to reference the UI components.
Their superview will hold an array of subview.(hold the array of subviews, also hold each subview, Am I right?).
Therefore, I think we can just use a weak reference to reference the picker which is an subview of the main view. And the main view will hold the picker.
Apple recommends that outlets should be declared as weak references.
I seem to recall that the advice used to be the opposite. If so, it seems likely that your example was written when the recommendation was to use strong (or retain, if it originally predated ARC).
(And I don't think this is a duplicate, since this question is specifically in reference to outlets and not about the fundamental difference between strong and weak.)
From Apples iADSuite tabbed example there is a variable defined with delegate.
UIViewController<BannerViewContainer> *_currentController;
later it's cast as such
_currentController = (UIViewController<BannerViewContainer> *)_tabBarController.selectedViewController;
Whats the significance of using "BannerViewContainer" in the declaration, how it relates to the later cast and what's happening under the covers here?
Regards
Jim
There's nothing to do with delegates here. BannerViewContainer is a protocol. (You might be confused because delegation is often defined via protocols.)
Declaring a variable or parameter with an angle-bracketed protocol name means that anything assigned to it must be an object which conforms to that protocol: if you try to pass an instance of UIViewController or some subclass thereof, you'll get a compiler warning unless that instance is of a UIViewController subclass which declares conformance to the BannerViewContainer protocol. (That is, you can pass an instance of FooViewController if its header file reads #interface FooViewController : UIViewController <BannerViewContainer>.)
The cast you see later follows the same pattern as many casts: it's a case where the programmer knows that the object he's assigning meets the requirements for that variable, but the reference he's using doesn't have a matching declaration. That is, the tab bar controller only knows that its selected view controller is a UIViewController (or any subclass thereof), but the programmer knows that the views he put into the tab bar are all UIViewController subclasses conforming to the BannerViewContainer protocol.