I looked up this topic on google but have not got an understandable answer yet, the problem is that I know that when two classes are coupled together by instantiating an object in the first class from the second class and declaring another object in the second class from the first class this will cause a retain cycle that should be broken by using the keyword weak or unowned , yet I can not apply this way of thinking on the IBOutlets being declared as weak
for example
class SignUpViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var signUpBttn: UIButton!
}
this is an outlet in my viewController class, why the outlet is declared as weak ? as per what I understand is that to have a retain cycle, the uibutton class should have an object from the viewController class so that the two classes (viewController and uibutton) become coupled together
can anybody clarify what is happening under the hood?
all ui element in a view controller are part of the viewController view. so view is a UIView class and outlets are reference in the viewController to UIView class elements, so if the view is removed form the view hierarchy all cross element should be weak to avoid cross reference. thew eid problem about this is the reference from apple MVC, where a ViewController is a Controller but have a bunch o related code of the view part. all you outlets should be placed in the UIView class of you ViewController.
TLDR - in my opinion it's not a good decision.
A strong reference denotes ownership. The view or view controller is the owner of the views therefore the logical choice should be strong.
However, in most situations this does not really matter because the views are also strongly referenced by their parent in the view hierarchy.
When does it matter? It matters in situations when you are dynamically updating the view hierarchy by removing views/constraints etc. Once you remove a view/constraint from the hierarchy and you don't have a strong reference to it, it will get removed from memory.
Also note that the combination of weak and ! is a bit dangerous because ! denotes a reference that you expect never to be nil.
This can lead to errors, for example:
#IBOutlet weak var constraint: NSLayoutConstraint!
...
constraint.isActive = false // removes constraint from hierarchy, assigns `nil` to constraint
...
constraint.isActive = true // crashes the app
Personally, I always make outlets strong. For any weak references I always use ? and not !.
Note that weak in this case doesn't have anything to do with protection against reference cycles. It was just a personal decision by Xcode developers.
Historically, there might be a connection with UIViewController.viewDidUnload. However, that method is never called since iOS 6.
Apple recommends that an #IBOutlet be declared as strong. Many discussions / articles can be found about this.
If you take a look at this video from Apple's 2015 WWDC, right around the 32:30 mark: https://developer.apple.com/videos/play/wwdc2015/407/
He states:
In general you should make your outlet strong, especially if you are connecting an outlet to a sub view or to a constraint that's not always going to be retained by the view hierarchy. The only time you really need to make an outlet weak is if you have a custom view that references something back up the view hierarchy and in general that's not recommended.
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?
Looking at this,
#IBOutlet weak var label1 : UILabel!
Why is UILabel an optional? The fact that I established a IBOutlet connection to that UILabel means that the UILabel DEFINATELY exists, and wouldn't be nil. So why is it an optional UIlabel? and not UILabel?
It's becouse non-optional variables have to be initialized in constructor and leave all lifecicle.
IBOutlets are weak also, what means it can become nil while e.g. view contoller are still alive.
This is about how The View Controller life cycle actually works into play.
Let's be known of Model, View, and Controllers, before proceeding.
The View Controller object itself gets instantiated before the view is loaded i.e. your label. During this time, since view is not even loaded, there is no reference to the label, as it still needs to be created from storyboard.
For this reason, the object becomes nil for a pretty short unnoticable moment. And, when the view is loaded: ViewController, viewDidLoad(), then only the labels reference is assigned to the variable.
This outlines a short description on why optionals are really used in iOS.
have a longer question than usual.
I am attempting to make a card game and I am confused as to whether I am designing it awfully.
The player's hand consists of 10 cards, each of which is a button and 4 labels that indicate the stats of the card.
What I had in mind was creating an array of objects that have these UILabel and button properties. For example in a "Card" class I would have...
#IBOutlet var cardName: UILabel
#IBOutlet var cardStat1: UILabel
#IBOutlet var cardStat2: UILabel
#IBOutlet var cardStat3: UILabel
#IBOutlet var cardStat4: UILabel
I would then have an array that contains ten objects of this Card class. This is my design portion of my question.
My View Controller question is like I mentioned above - I have a Card class but I am having trouble connecting outlets to it. It inherits from UIViewController but I get the error...
Must call a designated initializer of the superclass 'UIViewController'
...even though I call
super.init()
Feel free to aid me with any portion of my issues, thanks a lot!
This is my design portion of my question.
Having 10 view controllers, one for each card, sounds like a solution that's far more complicated than it needs to be. I think you'd be better off with something like a collection view, with one collection view cell for each card. You'd also need some kind of data model for storing the information about each card -- an array of dictionaries, for example, or an array of your Card objects (but not derived from UIViewController).
I get the error...
The designated initializers for UIViewController are -initWithNibName:bundle: and -initWithCoder:. There's an -init method defined, but it sounds like Swift wants you to call a designated initializer, and that's not -init.
IBOutlets are weak by default in Swift. I have an object in the viewController Created in the storyboard which is not in the view hierarchy , So I need it to be a strong reference in ViewController , How can I Change #IBoutlet property to strong.
You can make an IBOutlet strong by either selecting strong when connecting the outlet:
Or just remove the weak keyword from the declaration:
#IBOutlet var label: UILabel!
Change the name of the outlet, it might be a reserved name so you are trying to override it.
As of Xcode 6 beta 2, Swift does not have a way to designate strong outlets. The workaround is to connect the outlet in IB, then remove the #IBOutlet attribute from your source file.
Update: This has been added in Xcode 6 beta 3.
Its now an option when creating the outlet from a drop down.
Here is why we might want to start making them strong from WWDC 2015 Session 407
http://asciiwwdc.com/2015/sessions/407
And the last option I want to point out is the storage type, which can
either be strong or weak.
In general you should make your outlet strong, especially if you are
connecting an outlet to a sub view or to a constraint that's not
always going to be retained by the view hierarchy.
The only time you really need to make an outlet weak is if you have a
custom view that references something back up the view hierarchy and
in general that's not recommended.
So I'm going to choose strong and I will click connect which will
generate my outlet.
The strong keyword is gone again and raises a syntax error in Xcode 6.1.1. It appears like outlets are now strong by default, which previously was the opposite. So simply define the outlet w/o additional declaration.
#IBOutlet var nameOfOutlet: type = Whatever();
Another reason for that error is because a stupid bug. For example when I initialize a UIImageView object called "imageView" in the view controller an error occurred called "cannot override strong property with weak property".
But when I change the object name for example "pictureView", the error is gone.
Best regards...
As of Xcode 6 beta 3, Swift now allows marking #IBOutlets as strong.
From the release notes:
• #IBOutlets may be explicitly marked strong to override their
implicitly-weak behavior. (16954464)