swift: handling unwrapped optional in viewDidLoad - ios

I have actually some troubles handling unwrapped optional constant on UIViewController.
Because UI components exist only after viewDidLoad calls, It seems I can't use the 'let' constant modifier on my constant variables who need GUI dependancy.
Here is an example:
class ViewController: UIViewController {
#IBOutlet weak var blueSquare: UIView!
var animator:UIDynamicAnimator!
required init(coder aDecoder: NSCoder) {
// If animator was constant, it should be initialized here.
// But blueSquare is not initialized at this time, so I can't
// call UIDynamicAnimator(referenceView: blueSquare)
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
// At this time, the initialisation is ok. But animator is now
// actually modifiable
self.animator = UIDynamicAnimator(referenceView: blueSquare)
}
}
As you can see, animator is written as if it can be modifiable. The intend isn't.
My question is: is there any nice pattern who can put 'animator' as constant ? I didn't find any documentation dealing with viewDidLoad and constants with GUI dependency paradigm.

That is why you should leverage optionals.
When you declare:
var animator:UIDynamicAnimator!
You make the compiler believe that animator doesn't have to be initialized before calling init(coder). That's a dangerous game you are playing here. Instead you'd much rather use:
var animator:UIDynamicAnimator?
so using the var before it is initialized is reported as an error. Then you can set it up in viewDidLoad and things are back to where they should be.
You are right about assuming that blueSquare can be used only later in the controller's lifecycle and this has nothing to do with Swift syntax; it is a framework constraint. Therefore you need to have animator declared as a dangling reference till it can be inited to something useful. That's what optional provides to you.

Related

Swift: 'super.init' isn't called on all paths before returning from initializer?

I am getting this error on the last brace of a init in a class of mine. The class looks something like the following (I market the spot where error happens):
class RecordingViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
let cameraButton:UIButton?
let camPreview:UIView?
init (cameraButton: UIButton!, camPreview: UIView!) {
self.cameraButton = cameraButton
self.camPreview = camPreview
} //get error here
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//do a bunch of other stuff
}
I have looked here and here for a solution but both seem like solutions that are either really bad or that are too specific to that question, thus they have not work for me.
I was hoping for a solution to my problem done in such a way that it can help me understand why this error is happening.
Since you inherit from UIViewController, you should call super.init right after you set the variables in your init function
When you inherit a class and implement a new init function or override its own init function you should (almost) always call super.init. Let's take your example, you inherited from UIViewController. UIViewController has a few init functions that you can use to initialize a view controller. if you don't call super.init, all the code inside those functions will not get called and possibly the view controller won't get initialized.
Anyway, this piece of code should work for you:
class ViewController: UIViewController {
var button: UIButton?
init(button: UIButton) {
self.button = button
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is what I found on Swift Programming Language:
In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
Hope this can explain that question.

Lazy property with UIView subclass

I want to add UIView subclass property with lazy initialization, for example:
import UIKit
class MyView: UIView {}
class Controller: UIViewController {
lazy var myView = MyView()
}
But I have an error:
Cannot convert values type 'UIView' to specified type 'MyView'
I can fix the error with type of property:
lazy var myView: MyView = MyView()
or change initialization to:
let myView = MyView()
but why Swift cannot inference the type?
The important thing is providing a type if you are an initialized to a variable marked lazy.
lazy var myView:MyView = MyView()
I tried to replicate the issue but with custom class. And did not found any issues.
One thing to be noted is, when the lazy property had no customisations (the defaultValue in sample) compiler did not asked me to provide the explicit type.
But
For the property with customisation (redView), I had to provide the explicit type.
If I did not provide explicit type here is what I got.
Unable to infer complex closure return type; add explicit type to
disambiguate
And this says clearly enough, that the closure's return type cannot be inferred. Its seems obvious because the closure we are using has no explicit return type.
So I tried to supply a closure with explicit type and I was expecting now that now I would not need to provide the explicit type for the redView lazy property. And as expected, it worked without supplying the type for the lazy property.
if you provide init for MyView, then it will be ok.but why? I spent hours to figure out, the result is 😭, waiting for the master to answer.
class MyView: UIView {
init() {
super.init(frame: .zero)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Initializing UIView init AFTER its superclass init?

Looking at the a lecture slide in the Stanford iOS 9 course here, he is creating a new UIView with two initializers (one if the UIView was created from storyboard, and one if it was created in code). The following code is written at the bottom of that particular slide:
func setup() {....} //This contains the initialization code for the newly created UIView
override init(frame: CGRect) { //Initializer if the UIView was created using code.
super.init(frame: frame)
setup()
}
required init(coder aDecoder: NSCoder) { //Initializer if UIView was created in storyboard
super.init(coder:aDecoder)
setup()
}
The rule is that you must initialize ALL of your own properties FIRST before you can grab an init from a superclass. So why is it that in this case he calls his superclass init super.init BEFORE he initializes himself setup()? Doesn't that contradict the following rule:
Safety check 1 A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
As mentioned above, the memory for an object is only considered fully initialized once the initial state of all of its stored properties is known. In order for this rule to be satisfied, a designated initializer must make sure that all its own properties are initialized before it hands off up the chain.
I haven't seen all the rest of the code in this example, but the rule is only that your properties have to be initialized (i.e. the memory they occupy has to be set to some initial value) before calling super.init(), not that you can't run extra setup code.
You can even get away with sort of not-really-initializing your properties by either declaring your properties lazy var, or using var optionals which automatically initialize to nil. You can then set them after your call to super.init().
For example:
class Foo: UIView {
var someSubview: UIView! // initializes automatically to nil
lazy var initialBackgroundColor: UIColor? = {
return self.someSubview.backgroundColor
}()
init() {
super.init(frame: .zero)
setup() // do some other stuff
}
func setup() {
someSubview = UIView()
someSubview.backgroundColor = UIColor.whiteColor()
addSubview(someSubview)
}
}

Strongly referenced variable may cause memory issues

I have been programming in Swift for a couple months now. Recently, I have focused more on concepts of how Swift as a language works.
Hence, recently while reading apple documentation on Automatic Reference Counting(ARC), I came across the following lines:
This one on top:
In most cases, this means that memory management “just works” in Swift, and you do not need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.
And in the next paragraph, the following:
To make this possible, whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance. The reference is called a “strong“ reference because it keeps a firm hold on that instance, and does not allow it to be deallocated for as long as that strong reference remains.
I am a little confused as to what is the dynamics of the situation. I have noted while using storyboards, that you set reference to weak, hence the class looks like this, also what I would call case 1:
Case 1
class SomeClass : UIViewController {
#IBOutlet weak var nameLabel : UILabel!
override func viewDidLoad() {
nameLabel.text = "something."
}
}
Here, the label has one-to-one weak reference with the ViewController, and as soon as the Controller is changed, reference is broken (memory dealloc) since it is weak. Hence, no issues related to the memory.
Pardon me if the above statement is wrong or loosely held. I would be glad if someone confirms my assumption or counters it.
My question is about the second case however, where I do not use storyboards and class looks like below:
Case 2
class SomeClass : UIViewController {
var nameLabel : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
view.addSubView(nameLabel)
// view.addConstraints...
}
}
For the above case, My assumption is that the ViewController has one-on-one strong reference with the label, and the view inside ViewController also has strong reference with the label.. If the class is changed/ label is removed from subview.. then I think the memory would not be deallocated. Or at least the view controller will maintain a strong reference to the label (as per the docs.)
I confirmed this by removing label from view's subviews and printing out the label (It gave me an instance of UILabel with frame that was at 0 origin and 0 size.) hence an instance that isn't nil.
The only thing I could gather from this was that although the label was removed from UIView, it still maintained a strong reference with the controller, hence permanent state in memory. Am I right?
If this is the case. How should I prevent my code from having such memory issues? The bigger problem is that if I declare my variable like so, I get a nil while adding it as a subview of main view in controller.
weak var nameLabel : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
If declaring variables like in the second case can cause permanent strong references how should I declare them instead to not have memory issues?
So to conclude, my question is:
In cases where no storyboard outlets are used, and variables are strongly referenced to the view controller, will these references cause memory issues?
If so, what code declaration practice must I follow?
If not so, please provide thoughtful arguments with valid explanations to counter it.
Again, pardon me if I am incorrect anywhere.
Thank you in advance.
The only thing I could gather from this was that although the label was removed from UIView, it still maintained a strong reference with the controller, hence permanent state in memory. Am I right?
No. There's no big issue here.
The label has no strong reference to the view controller — if it did, that would be a retain cycle and would cause both the label and the view controller to leak. For this very reason, a view should never keep a strong reference to its view controller.
Here, however, it's the other way around: the view controller has a strong reference to the label. That's fine. It's true that the label therefore stays in existence after it has been removed from its superview. But that might not be bad. In many cases, it's good! For example, suppose you intend to put the label back into the interface later; you will need to have retained it.
If you are sure you won't need to keep the label around later, then simply use an Optional wrapping a UILabel as your instance property. That way, you can assign nil to the label instance property when you're done with it, and the label will go out of existence.
But in any case there is no leak here and you should just stop worrying. When the view controller goes out of existence, the label will go out of existence too. The label lived longer than it had to, but that's tiny and unimportant on the grand scale of things.
create the label when you need ,then call addsubView to make an strong reference to it and make an weak reference to your member var like this:
class ViewController: UIViewController {
weak var label : UILabel?
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
view.addSubview(label)
self.label = label
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print(label)
//click first Optional(<UILabel: 0x7fb562c3f260; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fb562c11c70>>)
//click second nil
label?.removeFromSuperview()
}
}
anyway while the viewcontroller release ,the label will be release and view.subview will be release too.
Demo
i wrote an easy demo make the ViewControllerTest to be the rootviewcontroller
class Test{
weak var label:UILabel?
static let instance = Test()
}
class ViewControllerTest: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let item = UIBarButtonItem(title: "Test", style: .Plain, target: self, action: #selector(self.test))
self.navigationItem.rightBarButtonItem = item
}
func test(){
print(Test.instance.label)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let vc = ViewController()
self.navigationController?.pushViewController(vc, animated: true)
print(vc.nameLabel)
let test = Test.instance
test.label = vc.nameLabel
}
}
class ViewController: UIViewController {
var nameLabel : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
view.addSubview(nameLabel)
let item = UIBarButtonItem(title: "Test", style: .Plain, target: self, action: #selector(self.test))
self.navigationItem.rightBarButtonItem = item
}
func test(){
print(Test.instance.label)
}
}
I don't think strongly referenced variables to view controller cause any memory issues.
Normally views are deallocated before deallocating their view controller. For example, in in your code, when deallocating the view, ARC decreases the counter pointing to namelabel, so it passes from 2 to 1. Then, when deallocating the view controller it decreases the counter again, from 1 to 0. Once there are 0 references pointing to namelabel its removed.
A weak reference is a reference that does not keep a strong hold on
the instance it refers to, and so does not stop ARC from disposing of
the referenced instance. This behavior prevents the reference from
becoming part of a strong reference cycle. You indicate a weak
reference by placing the weak keyword before a property or variable
declaration
> Weak references must be declared as variables, to indicate that their
value can change at runtime. A weak reference cannot be declared as a
constant.
Because a weak reference does not keep a strong hold on the instance
it refers to, it is possible for that instance to be deallocated while
the weak reference is still referring to it. Therefore, ARC
automatically sets a weak reference to nil when the instance that it
refers to is deallocated. Because weak references need to allow nil as
their value, they always have an optional type. You can check for the
existence of a value in the weak reference, just like any other
optional value, and you will never end up with a reference to an
invalid instance that no longer exists
Source: Apple docs
A weak reference is just a pointer to an object that doesn't protect the object from being deallocated by ARC. While strong references increase the retain count of an object by 1, weak references do not. In addition, weak references zero out the pointer to your object when it successfully deallocates. This ensures that when you access a weak reference, it will either be a valid object, or nil.
Hope can help you to understand better a weak reference, be it related to a storyboard item or created programmatically.
I always explain it to my students like this.
With a strong reference, you can see a value, and you have a lasso around it. You have a say in whether the value remains alive.
With a weak reference, you can see it, but there's no lasso. You have no say in whether the value lives or not.
For your situation to avoid occurrence of Memory leak for a second. You can go with Matt answer.
For better understanding, create a custom UILabel class under MRC flag in build phases->Complie sources.
In custom class, override retain and release method. Put breakpoints on them.
Use that custom UILabel class in your view controller with ARC flag ON. Go with matt answer or use below optional declaration of UILabel.
import UIKit
class ViewController: UIViewController {
var label:UILabel? = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "something"
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.label!)
//namelabel goes out of scope when method exists.
//self.view has 1+ ref of self.label
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.label?.removeFromSuperview()//-1 ref of self.label
self.label = nil
print(self.label)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You will have clear picture of how ARC works and why weak ref of UILabel causes crash while adding to UIView.

Swift setting variable inline or in function?

What is the Swift standard for setting a variable you already know the value for? Here are the 2 different ways I'm thinking of.
Option 1: Declaring the variable in the class and then setting it in the ViewDidLoad method
class ViewController: UIViewController {
var refreshControl: UIRefreshControl!
var sampleString: String!
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
sampleString = "Hello"
}
}
Option 2: Declaring the variable in the class and setting it inline
class ViewController2: UIViewController {
var refreshControl = UIRefreshControl()
var sampleString = "Hello"
override func viewDidLoad() {
super.viewDidLoad()
}
}
Which is the preferred way to do this in Swift? Thanks in advance!
First of all, you have two fundamentally different types in your two examples. In the first example, the type is an implicitly unwrapped optional String (i.e., String!), which means it can accept the nil value. In the second example, it is just String. If the value does not need to be nil assignable, the second option is better.
With regard to your actual question. I would say the second option is preferable, as you initialize the value earlier and there is no chance that you will use it before it is initialized. This would be equivalently good to declaring the type as String and deferring the initialization to an init method.
The viewDidLoad method is only useful for UIViewController instances, and doesn't get invoked until the view is loaded (which typically is during presentation). Waiting to initialize a value until then is probably not preferred and wouldn't be useful in objects that don't subclass UIViewController.

Resources