Does defining functions inside viewdidload() save more memory versus defining them outside? - swift

Since viewdidload() is only called once in the lifecycle of that instance of the UIViewController object, does that mean that this example below is a "bad practice" since setBackgroundColor(), a function that is only called once, is unnecesarrily loaded into the memory of the entire class when it really should just exist entirely (defined and called) inside viewdidload()? Or in terms of efficiency, does it not matter where setBackgroundColor() is defined and called?
class MasterViewController: UIViewController {
func setBackgroundColor() {
self.view.backgroundColor = UIColor.green
}
// Do any additional setup after loading the view, typically from a nib.
override func viewDidLoad() {
super.viewDidLoad()
setBackgroundColor()
}
// Dispose of any resources that can be recreated.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Making a function local to a method changes its scope, but not the lifetime of code compiled from it. Same goes for methods of the class: their binary codes are not managed individually, at least not at this time. This is not a big deal, though, because executable code of your function is relatively tiny.
What matters here is that the function name is not visible in its outer scope, letting other methods define their own setBackgroundColor() functions unrelated to the one defined within viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Nested function definition
func setBackgroundColor() {
self.view.backgroundColor = UIColor.green
}
// Calling nested function
setBackgroundColor()
}
This improves readability, because function definition is right there at the point where it is used. It also improves maintainability, because whoever is refactoring your code can be certain that there can be no other uses of setBackgroundColor outside of viewDidLoad.
Of course, this is just an example. The nested function is not necessary here - you can rewrite it without setBackgroundColor function, like this:
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.green
}

It's not a case of memory usage, it's a matter of loading the view efficiently.
UIViewControllers control views. But the views isn't created at the same time as the view controller.
Setting the background colour, indeed, any kind of view configuration, in viewDidLoad is done because when that method is called, the view has been created (although not necessarily displayed) at a convenient point in the view controller's life cycle. If you created the view controller and then called your setBackgroundColor method, the self.view part of the call would cause the view to be created straight away if it hadn't already been created.
For certain methods, such as view controller presentations, this allows the creation methods to return as quickly as possible without the view being loaded straight away and keeps the UI responsive.
It's because of this lazy loading of views that UIViewController has the isViewLoaded parameter which returns a Bool for whether the view is loaded into memory or not, but without causing the view to load.

I don't see anything in your code that warrants a memory concern. Micro-optimization is greatest time waster in software development. But since you asked, viewDidLoad is invoked only once. From Apple's Work with View Controllers:
viewDidLoad()—Called when the view controller’s content view (the top of its view hierarchy) is created and loaded from a storyboard. The view controller’s outlets are guaranteed to have valid values by the time this method is called. Use this method to perform any additional setup required by your view controller.
Typically, iOS calls viewDidLoad() only once, when its content view is first created; however, the content view is not necessarily created when the controller is first instantiated. Instead, it is lazily created the first time the system or any code accesses the controller’s view property.

My guess is that any loss in efficiency is worth the gain in having more readable code. In fact, a compiler optimization may even inline the function if you mark it as private. I don't know enough about the Swift compiler (LLVM) to know if this is definitely true but it may be.
Martin Fowler has a great article on function length in which he states that he has many functions that are one line long simply because they make the code easier to understand.
Some people are concerned about short functions because they are worried about the performance cost of a function call. When I was young, that was occasionally a factor, but that's very rare now. Optimizing compilers often work better with shorter functions which can be cached more easily. As ever, the general guidelines on performance optimization are what counts.
Not sure if his notes about function caching apply to Swift, but he also says:
If you have to spend effort into looking at a fragment of code to figure out what it's doing, then you should extract it into a function and name the function after that “what”. That way when you read it again, the purpose of the function leaps right out at you
In general, I wouldn't focus too much on optimization unless you notice that it's a problem. YAGNI
... And as Code Different noted, yes ViewDidLoad() is only called once.

Related

Should I keep initializing a new ViewController every time or reset the old ViewController?

I have an app where the user has a list of stocks that they follow (so it can vary/change). When the user clicks on a cell in the list, it opens up a StockViewController() that shows the stock data. I've started to worry that initialing a new ViewController every time is bad practice and causing an increase in memory usage over time (Not even entirely sure if that's true).
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if(indexPath.section == 1) {
let stockVC = StockViewController()
stockVC.parentView = self
stockVC.stock = followingStocks[indexPath.row]
DispatchQueue.main.async {
self.present(stockVC, animated: true, completion: nil)
}
}
}
I have tried attempting to use the same ViewController by reseting the tableview/data inside of it after each use but am having trouble successfully making this a smooth process. Is doing it the above way going to cause issues in my app or should I try to use the same ViewController, below, each time?
class VCManager {
static var stockVC = StockViewController()
static func resetStockVC() {
stockVC.stockData = [StockData]()
stockVC.tableView.reloadData()
}
}
Initializing a new view controller every time is not only fine, but very common. When you dismiss that view controller, the memory for it is automatically deallocated, so you won't be increasing your memory usage overtime. (With the exception that you have a retain cycle in that view controller that could potentially cause a memory leak, but that's a whole different topic of its own).
I agree with Reza. Caching a view controller is uncommon. Creating a new instance of a view controller is really fast und laying out its views depends on the complexity of the view hierarchy.
Creating a new view controller every time you need not only has the advantage of consuming memory only when you need it and as long as you need it but it also removes state handling from it. I.e. you do no longer need to take care of updating your model and that your table view is in sync with the underlying model data aka making sure that your datasource is up to date with the model and tableView.reloadData(). Ideally you'd inject your model array as part of your initializer and store it as a property constant. Using a constant also makes your intent obvious for others that there will be no model changes to worry about.

Swift: Referencing variables in one class from another function and/or class

trying a few things out in Swift. I’m trying to get some things that seem muddled to me straightened out - mostly to do with how I deal with variables and referencing them in a project.
What I am trying to do is keep a variable (based on a struct) defined in ViewController accessed and updated from various other functions within an application.
So, a brief outline of the code I have is here. I actually wrote a smaller app to test my ideas out before applying them to something more complex.
I started in XCode with a Swift document based application for Mac OSX.
In ViewController.swift I have:
import Cocoa
class ViewController: NSViewController {
var myText = "Hello, some text"
#IBOutlet weak var textView1: NSTextField!
#IBAction func Button1(_ sender: Any) {
myText = "This is button 1 clicked"
myText = setText( thisText: &myText )
textView1.stringValue = myText
}
#IBAction func Button2(_ sender: Any) {
print("Button 2")
myText = "This is button 2 clicked"
textView1.stringValue = myText
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
textView1.stringValue = myText
}
override func viewDidAppear() {
let document = self.view.window?.windowController?.document as! Document
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
In SetText.swift, I have this:
import Foundation
func setText( thisText: inout String) -> String {
thisText = "Function"
return thisText
}
I liked the idea of sending variables to the Set Text function and then returning it, but thinking about it more makes me think that actually this could end up like a proverbial bowl of spaghetti with functions calling functions and who know what else. So I was thinking that something like this would probably make more sense:
import Foundation
func setText( thisText: inout String) {
let vc = ViewController()
// Read the variable from View Controller
var myTextHere = vc.myText
myTextHere = myTextHere + " More text"
// Set the variable in ViewController here
vc.myText = myTextHere
}
From my reading around on this subject, if I call ViewController(), it will create anew instance of the view (is that right, or am I misreading?). That’s already happened, so what I need is to reference the ViewController that called the function setText, or rather that owns that particular instance of code. As I’m thinking about a document based application, I’d obviously want to keep all instances of myText with each document’s ViewController.
My aim is to create something a bit more complex, using a variable based on a Struct to keep everything together. So:
myCard.image // holds a bitmap image
myCard.size // holds the size of the image
And so on. being able to access it in the form of ViewController().myCard to both read and write to is what I am thinking I need to do.
What I don’t want to do is use global variables.
Thanks.
I'm having a hard time seeing much correlation between the title of your question and the body of your question. In fact, I'm not even entirely sure that there's a question being asked. That said, I'll try to address the questions you appear to be asking (re-written how I think they're intended):
When you initialize a new view controller, does it create a new view?
Yes. There is a view property for every view controller, and it's not a shared component or a singleton or anything else like that. There is one main view for every view controller. It's almost certainly composed of dozens of other subviews, but there is one view that is the view for every view controller.
Is there a way to get metadata about the state of a view controller from outside, preferably in the form of a struct?
Absolutely. First, you'd need to define that Card struct. I'd recommend doing it in the same file as the view controller itself. You can define it outside of the view controller, or if you want stricter coupling and namespacing, you can do it inside the view controller. Just be aware that doing that latter would mean that the type name, when referenced outside the view controller, would be ViewController.Card rather than just Card.
Then you'd want to create either a computed property (var card: Card) or a method (func card() -> Card) on your view controller which builds and returns one of those based on the state. It sounds like you're already leaning toward the property approach.
Note: I would absolutely advise against having one that is a normal get/set property because then you'd have to constantly update it and modify it. The best thing to do is have a computed property which builds it on the fly. So when the property is called, it reaches into all your components to get the info you want (like image sizes, strings, etc) and then packages up and hands off the Card metadata struct. Doing it on-demand like this eliminates unnecessary complexity and consolidates the metadata logic into one place.
Some dangerous things in your code example:
I can't think of a good reason to implement viewDidAppear() but not call super.viewDidAppear(). Unless you have an enormously compelling reason to leave that out (I honestly can't think of a single one), do not do so.
I don't see any good reason for your first implementation of your setText(thisText:) method. The way you use it in your Button1(_ sender: Any) IBAction functionally does absolutely nothing. That method in general is screwy for several reasons: it's got an upper-case method name, sets the textView text by trying to assign to stringValue for some super strange reason, and does in three lines what could be done in one:
textView1.text = "This is button 1 clicked"
The second implementation of setText(thisText:) makes even less sense than the previous. The two biggest problems being 1) that you don't even use the thisText argument passed in, and 2) your method, which is called 'set text' is creating a whole new view controller every single time it gets called? That's a huge violation of "doing what it says on the tin." Methods should have a single responsibility and shouldn't do anything beyond that responsibility. I'd never in a million years look at a method called setText and think "I'll bet this initializes a view controller." Never.
I see this question has already been downvoted a bit (not by me), and I'd like to take a moment to coach you in using Stack Overflow: Ask clear, concise, specific questions about clear, concise, specific topics. As I said at the top of my answer, there doesn't appear to be a question anywhere in your post. I had to kind of make some up that I inferred from what you wrote.
Remember: coding isn't just wiggling your fingers while you think about an app. If you're doing the hard work of good engineering, you'll likely spend a ratio of about 10:1 (or more!) of staring at your screen to actually typing any code. Every time you write a line of code, you should be asking yourself, "Why am I writing this line of code? Is this necessary? Am I reinventing the wheel?)
Good luck!

Swift - Lazy Var vs. Let when creating views programmatically (saving memory)

I'm a beginner and I sort of understand Lazy Var vs. Let. I've noticed that it saves a ton of memory usage when using Lazy Var especially with ImageViews. But the tutorials and guides I've seen so far don't use Lazy Var very often, so I'm feeling suspicious that it is bad practice and that I'm overlooking something.
I did a little research and learned that Lazy isn't "thread safe," but I don't understand what this means. I've seen a lot of pros and cons, but I can't draw any conclusions especially because I have very limited knowledge.
When is it ok (or better) to use Lazy Var vs. Let when creating a UIView?
lazy var profileImageView: UIImageView = {
let imageView = UIImageView(image: #imageLiteral(resourceName: "page1"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
Whether you will use lazy var or not depends on your code and its context. It is not bad or good on its own. You have to decide when it is appropriate.
Before you can decide that, you have to know what lazy var is.
What is lazy var?
Lazy initialization is a concept where initialization (construction) of variable content is delayed until its first usage. First access to such variable triggers initialization. Since content is not created until variable is used (needed) using lazy initialized variables can save resources.
That is primary drive behind lazy initialization. You don't create something until you need it. That is also logic you will use when deciding whether something should be lazy var or not.
If you are dealing with views (or anything else) that are always visible (needed) there is little point in using lazy initialization. On the other hand when you are dealing with instances that are not always needed - then using lazy var is justified.
If your view is always visible in presented view controller, you will not accomplish much by making it lazy. If it is visible only under specific circumstances - for instance when user expands some collapsed panel - then making it lazy makes sense. It will make your view controller load faster and use less memory by default.
As far as thread safety is concerned, lazy var are not thread safe in Swift.
That means if two different threads try to access the same lazy var at the same time, before such variable has been initialized it is possible that one of the threads will access partially constructed instance.
You can find more about thread safety in:
Swift - is lazy var thread-safe?
Make "lazy var" threadsafe
Another advantage to using a lazy var is improving the readability of your code.
In your example, the code related to the image view is grouped together instead of being spread out to an initializer, setup function, or viewDidLoad. This improves local reasoning by not requiring the reader of the code to venture to various places in the code to understand how your view is configured. To learn about your view, they only need to jump to its declaration.
An initialization closure marked as a lazy var can access self, allowing for more configuration to be done inside the closure, such as adding target actions or referencing other constant properties.
I would consider initializing properties (especially views) with closures as lazy var's to be a good practice, and it seems to be gaining popularity in the Swift community as well.
Depending on the project, saving developer time can be much more valuable than saving system memory.
The use of lazy vars can provide a workaround to a paradoxical problem: You want to create a custom view that has subviews whose initialization refers to the parent view.
For example, if you want to create a subclass of UIView that contains a child UIScrollView of the same size, you can't declare a class containing:
var m_scrollView: UIScrollView
override init(frame: CGRect)
{
m_scrollView = UIScrollView(frame: self.frame)
super.init(frame: frame)
}
The compiler will complain that you're referring to self before calling super.init. But... super.init has to be called after all members are initialized.
The solution to this circular problem is making m_scrollView lazy and initalizing it in its declaration:
lazy var m_scrollView = UIScrollView(frame: self.frame)

How do set a relationship between managed objects within an MVVM pattern?

I'm building an app and trying to use the MVVM pattern. While there's a plethora of information about how to wire up things up for data to flow form the model to the view model to the controller to the view, I'm having a very hard time learning how to do other things while sticking to MVVM principles. One thing I'm really struggling with is setting a relationships between two managed objects after segueing to different view controllers. Let me explain...
When my app starts, it presents the first view controller which is backed by a view model which talks to a newly created NSManagedObject of type LiftEvent. From here, the user can segue to a Settings view controller which also has a view model, and from there they can segue to a table view, which also has a view model, and make a selection that needs to change an NSManaged var property of the model object. This property is a relationship to another NSManagedObject. Here's the flow:
I'm passing the managedObjectContext from the first view controller to the Settings view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let nav = segue.destinationViewController as? UINavigationController {
let vc = nav.topViewController as! SettingsViewController
vc.dismissalDelegate = self
let moc = viewModel.model.context
vc.moc = moc
}
and then pass it again to the last view controller where the user makes a selection and I want to set the relationship. It works, but I don't like it because I now have to import CoreData in the view controllers I'm passing it to.
The view controller on the far right is a UITableViewController. When the user selects one of the rows, I want to use that selection to grab the corresponding managed object and set it as one of the properties on the object that was created when the app launched. Here's the didSelectRowAtIndexPath method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let defaultFormula = NSUserDefaults.formula()
guard indexPath.row != defaultFormula.rawValue else { return }
if let newDefaultFormula = FormulaName(rawValue: indexPath.row) {
NSUserDefaults.setDefaultFormulaName(newDefaultFormula)
}
if let selectedFormula = formulasArray[indexPath.row] {
// now what?
}
}
FormulaName is a enum of the possible names. The data source is formulasArray, an array of Formula managed objects. I initialize the array in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
formulasArray = dataManager.fetchSelectableFormulas(moc)
}
I see a few possible solutions in my head but I have no idea which of them, if any, are good choices:
pass the Formula object I picked out of the array back to the first view controller in unwind segues and set the relationship there
somehow get a hold of the LiftEvent from here, set the relationship, then tell the view model of the first view controller that the object has changed.
pass a reference to the LiftEvent object around instead of passing just the managedObjectContext property
I've spent countless hours trying to find examples, lessons, git repositories, SO threads, etc to figure this out that I don't know where to go from here. I'm not asking anyone to write the code for me, just steer me in a good direction and I'd be forever grateful.
I too have had issues finding concrete examples of MVVM in iOS, so i've kind of developed my own approach.
I have a couple recommendations (this is opinion to be clear).
Keep the NSManagedObject context in a CD access singleton for access on the main thread is really helpful. Assuming you're not using multiple databases or doing a bunch of things in the background, (which you can handle as well with the accessor if necessary) you will have 1 context with no context passing.
When using Segues, dependency injection is awesome. Rather than giving your new VC the minimum information it needs to find the data and set things up, simply set it's ViewModel in the Segue and build the VC to work only off of that. This is what makes ViewModels work so well. your View/ViewController doesn't need to to know anything about your model.
When dealing with unwinds, my approach is to simply make a "selectedItem" property in the desired VC and leave the responsibility of what to do to the VC to which it is unwinding. If necessary it can grab that value when the unwind occurs. This allows your choosing VC to operate the same way regardless of the behavior around it.

Alternative to global variables in app delegate

I am developing an application with a speedometer like animation (a meter and an arrow to indicate something on the meter). I store the arrow's position in a global variable declared in my app delegate. I am doing it this way because the arrow position is updated and used by several classes.
However, I am insecure whether this is a good or a bad design decision. My thoughts are that since it is a non-critical piece of information (just a float), no harm is done when storing it globally. But my OOP heart hurts every time I say the word "global" to myself.
Alternatively I have studiet singletons, but as far as I have read, singletons are used when the developer wishes to create one and only one instance of a certain object.
Am I doing it correct or is there a more proper way of doing what I do?
I am doing it this way because the arrow position is updated and used by several classes.
in many cases, you can reduce the scope. this reduces inter-component dependency.
However, I am insecure whether this is a good or a bad design decision. My thoughts are that since it is a non-critical piece of information (just a float), no harm is done when storing it globally. But my OOP heart hurts every time I say the word "global" to myself.
perhaps you can move the state (the float value) to an ivar in your speedometer? example: you likely display just one speedometer view: does it make more sense to add it to what is the view's model? or perhaps to its controller? (yes, it's a bit tough to provide a more specific example without the source)
Alternatively I have studiet singletons, but as far as I have read, singletons are used when the developer wishes to create one and only one instance of a certain object.
not necessary, and a severe pain to maintain. most of the cocoa singletons i have seen should not have been considered singletons, and caused a lot of headaches. better yet, you can write programs which use zero singletons. this is ideal, and easy to test. as is, the programs/types which depend on the app controller's have been compromised wrt testability and reusability.
Am I doing it correct or is there a more proper way of doing what I do?
in the vast majority of cases, you can simply reduce the scope and localize it, while removing global state. with a little more effort, you can remove that value as a global -- that is best.
although it is not a good thing... let's assume you really really really really really must introduce global state:
don't use a singleton. chances are good that you will rewrite it when you want to reuse it. it sugar coats what is ugly. if your app controller is a mess due to too much global state, at least the fact that you have too much global state will be obvious.
hold your global state in your app controller. your app controller is responsible for its initialization, lifetime, and access.
provide that state to dependencies, so they do not refer back to (or even know about) the global domain (the app controller). then you may minimize the impact.
there's also a distinct difference between global state and application/execution state. global state should be eliminated. execution state is not global state, but localized execution context. execution state can be reintroduced at the right level, altered, and updated, tested, and reused predictably. a good design will introduce execution state when needed, and at the right level while avoiding global state.
Update
Your sample is pretty close to what i had imagined, based on the description in the OP. It provided some additional specifics. So the sample below (you'll need some additions in obvious areas to piece it all together) demonstrates how you could update the controller interfaces, and there are two free 'elsewhere' methods at the end which further illustrate how to use these:
#interface MONArrowPosition : NSObject
{
float arrowPosition;
}
#end
#implementation MONArrowPosition
- (id)initWithPosition:(float)position
{
self = [super init];
if (nil != self) {
arrowPosition = position;
}
return self;
}
#end
#interface MyViewController1 : UIViewController
{
MONArrowPosition * arrowPosition; // << may actually be held by the model
}
#end
#implementation MyViewController1
- (void)applyRotation
{
[self rotateLayer:arrow from:self.arrowPosition to:callStatus speed:METER_SPEED];
}
#end
#interface MyViewController2 : UIViewController
{
MONArrowPosition * arrowPosition; // << may actually be held by the model
}
#end
#implementation MyViewController2
- (void)viewDidLoad
{
[super viewDidLoad];
/* ... */
[self.slider addTarget:self action:#selector(sliderValueDidChange) forControlEvents:controlEvents];
}
- (void)sliderValueDidChange
{
self.arrowPosition.arrowPosition = self.slider.value;
[self arrowPositionDidChange];
}
#end
/* elsewhere: */
- (void)initializeArrowPosition
{
/* The variable is set to a default of 0.0f */
MONArrowPosition * arrowPosition = [[MONArrowPosition alloc] initWithPosition:0.0f];
/* ... */
}
- (IBAction)someActionWhichPushesMyViewController1
{
// depending on the flow of your app, the body of initializeArrowPosition
// *could* be right here
MyViewController1 * viewController = [[MyViewController1 alloc] initWithNibName:nibName bundle:bundle];
viewController.arrowPosition = self.arrowPosition;
/* push it */
}
and then if MyViewController1 pushes MyViewController2, locating and setting the arrow position will be easy. the view controllers may also be sharing some information in the models. with a global in your sample, you are crossing many implementations, which adds coupling, increases dependency, etc.. so if you can take this approach and localize the execution state, you're off to a good start. then you can use any number of view controllers with any number of MONArrowPositions, and they will not be subject to the effects of global state. again, i can't get too specific using the samples provided, but i think this should illustrate the concepts i originally outlined well enough (i don't think a project-wide review is needed).
Well this is something that is keeping a lot of programmers up at night.
I try not to misuse the app delegate as much, I'll create a singleton for storing more or less global information. There is no real other way to do it then either the singleton or the app delegate.
But if only one viewController need the information than, the information will never leave that viewController. That viewcontroller could pass that information on to other viewcontroller is needed.
In you case it might be an idea to have some kind of directionManager which hold the floats and might even hold the CLLocationManager.
For this type of thing, I like to use NSNotifications. You have all the view controllers who care about the arrow's position listen for the specific notification, and they can all update the UI at once.