What is the difference between a Lazy or Optional property in Swift?
For example, if someone is building a navigation bar that comes in from the side, I think that should all be within one UIViewController. The user might never open the menu but sometimes they will.
var menu: NavigationBar?
lazy var menu: NavigationBar = NavigationBar.initialize()
Both of the optional I think are good code, because they don't create the view unless its needed. I understand Optional means there might be a value it might be nil. I also understand Lazy means don't worry about it until I need it.
Specific Question
My question is are their performance patterns (safety and speed) that say optionals are faster and safer or vise versa?
OK, this is an interesting question, and I don't want to imply that the existing answers aren't good, but I thought I'd offer my take on things.
lazy variables are great for things that need to be setup once, then never re-set. It's a variable, so you could change it to be something else, but that kind of defeats the purpose of a lazy variable (which is to set itself up upon demand).
Optionals are more for things that might go away (and might come back again). They need to be set up each time.
So let's look at two scenarios for your side menu: one where it stays around while it's not visible, and another for when it is deallocated.
lazy var sideMenu = SideMenu()
So the first time the sideMenu property is accessed, SideMenu() is called and it is assigned to the property. The instance stays around forever, even when you're not using it.
Now let's see another approach.
var _sideMenu: SideMenu?
var sideMenu: SideMenu! {
get {
if let sm = _sideMenu {
return sm
} else {
let sm = SideMenu()
_sideMenu = sm
return sm
}
}
set(newValue) {
_sideMenu = newValue
}
}
(Note this only works for classes, not structs.)
OK so what does this do? Well it behaves very similarly to the lazy var, but it let's you re-set it to nil. So if you try to access sideMenu, you are guaranteed to get an instance (either the one that was stored in _sideMenu or a new one). This is a similar pattern in that it lazily loads SideMenu() but this one can create many SideMenu() instances, where the previous example can only create one once.
Now, most view controllers are small enough that you should probably just use lazy from earlier.
So two different approaches to the same problem. Both have benefits and drawbacks, and work better or worse in different situations.
They're actually pretty different.
Optional means that the value could possibly be nil, and the user isn't guaranteeing that it won't be. In your example, var menu: NavigationBar? could be nil for the entire lifetime of the class, unless something explicitly assigns it.
Lazy on the other hand means that the assignment will not be called until it is first accessed, meaning that somewhere in code someone tries to use your object. Note however that it is STILL promised to not be nil if you declare it like you have here lazy var menu: NavigationBar = NavigationBar.initialize(), so no need to do optional chaining.
And actually, a variable can be BOTH Lazy AND Optional, which means that it's value will be loaded when it is first accessed, and that value might be nil at the point it's initialized or at any future point. For example:
lazy var menu: NavigationBar? = NavigationBar.initialize()
That NavigationBar.initialize() is now allowed to return nil, or someone in the future could set the menu to be nil without the compiler/runtime throwing errors!
Does that make the difference clear?
Edit:
As to which is BETTER that's really a case by case thing. Lazy variables take a performance hit on first initialization, so the first access will be a slow one if the initialization process is long. Otherwise, they're nearly identical in terms of safety/performance. Optional variables you have to unwrap before using and so there is a very minor performance cost with that (one machine instruction, not worth the time to think about)
Optional and lazy properties are not the same
An optional property is used when there are chances that the value might not be available(i.e can be nil). But in your scenario, the navigation bar will always be available, its just that the user might not open it.
So using a lazy property serves your purpose. The NavigationBar will only be initialised if the user taps on it.
I do not see any performance issues except that if you use an optional, there is an additional overhead of checking if the value is nil each time before accessing it.
Related
When it launches, the first thing our app does is to load a user object from our backend, while showing a splash screen.
Once that user has been fetched, our home screen is shown and we can consider that this user is valid. However, this property can only be optional in our codebase since it doesn't have a default value, so swift forces us to either mark it as optional or implicitly unwrapped.
We don't want to make it implicitly unwrapped because then if future developper were to add code using the user object that is actually ran before the object is loaded from the server, the app would crash.
My question is then, is there a design pattern on iOS that would let swift understand something like: first run that small part of the codebase that loads the user, then consider the rest of the project can be ran knowing the object is valid ?
Once that user has been fetched, our home screen is shown and we can consider that this user is valid. However, this property can only be optional in our codebase since it doesn't have a default value, so swift forces us to either mark it as optional or implicitly unwrapped.
It can be non-optional if you programmatically create your home screen and add User to its init. (This fairly straightforward idea often goes by the IMO confusing name of "dependency injection" rather than the obvious name of "passing parameters.") This tends to spread through the whole system, requiring that most view controller be programmatically created. That does not mean you can't use Storyboards (or simpler XIBs), but it does typically mean you can't use segues and will need to handle navigation more programmatically to pass your properties through the system.
This approach lets you make strong requirements for the existence of various properties at each point that you need them. It is not all-or-nothing. You can have some screens that use optionals, some that use implicitly unwrapped optionals, and some that are programmatically created. It's even possible to evolve an existing system over to this a few pieces at a time.
To your last paragraph, there is a common technique of a shared instance that is implicitly unwrapped and gets assigned early in the program's life. You then have to promise not to call any of the "main" code before you initialize it. As you note, that means if it isn't called correctly, it will crash. Ultimately there is either a compile-time proof that a value must exist, or there is only a runtime assertions.
You prove that a value must exist by passing it. You implicitly assert the the object should exist by using !. If it's possible for a developer to call the code incorrectly, then it's not a proof.
The correct design pattern is exactly what you are doing. When you have a data that won't be present at initialization time, make the property that holds it an Optional. nil means the data is not yet present; non-nil means that it is. This is completely standard stuff. Just keep doing it the way you are doing it.
If what bothers you is the fact that you have to fetch this thing as an Optional in every method that deals with it, well, tough. But at least the Swift language has nice techniques for safe unwrapping. Typically the first thing you will do in your method is to unwrap safely:
func f() {
guard let myData = self.myData else { return }
// and now myData is not Optional
}
The only I thing I would advise against here is the splash screen. What if the data never arrives? You need to show the user something real, perhaps with actual words saying "please wait while we fetch the data". You need to plan for possible timeout. So, what I'm saying is, your proposed interface might need some work. But your underlying technique of using an Optional is exactly right.
I have a class that looks like this (simplified):
class GuideViewController: UIViewController, StoreSubscriber {
var tileRenderer: MKTileOverlayRenderer! // <------ this needs to be set by whoever instantiates this class
override func viewDidLoad() {
super.viewDidLoad()
...
}
}
My app uses this GuideViewController class to display many different styles of maps, so the tileRenderer instance variable can have many different values.
I want a compile-time guarantee that tileRenderer will never be nil, instead of using an implicitly-unwrapped optional.
How can I achieve this?
Things I've considered so far but am unsure about
Setting tileRenderer in the init() method of the GuideViewController. This was my first instinct by this answer implies that this is not possible, or an antipattern.
Setting tileRenderer in viewDidLoad(). This seems to require using an implicitly unwrapped optional which bypasses compile-time checks. Also, I'm under the impression that viewDidLoad() is only called once for the view controller in the lifecycle of the app
Manually setting tileRenderer after instantiating the VC. E.g.,
let vc = self.storyboard?.instantiateViewController(withIdentifier: "GuideViewController")
vc.tileRenderer = MKTileOverlayRenderer(...) // <----- can I make the compiler force me to write this line?
navigationController?.pushViewController(vc!, animated: true)
Forgive me for asking such a naive question—I'm fairly new to iOS development.
It isn't possible for there to be a compile-time check, since that would require the compiler to completely analyse the flow of your program.
You can't make the property a non-optional (well, you can - see point 4), since that requires a custom initialiser, which doesn't really work with UIViewController subclasses.
You have a few choices:
Use an implicitly unwrapped optional and crash at runtime if it is nil - hopefully the developer will quickly identify their mistake
Check for nil at a suitable point (such as viewWillAppear) and issue a warning to the console, followed by a crash when you try and access the value or call fatalError - This will give the developer more hints as to what they have done wrong
Use an optional and unwrap it before you use it
Use a non-optional and provide a default value
Option 4 may be the best option; Provide a default render that does nothing (or perhaps issues warnings to the console log that it is a default renderer and that the developer needs to provide one).
Currently when making an ios app in Titanium, I have to pass the navgroup into all windows.
That means always passing the navgroup through the entire function call stacks to the part where a window is launched.
function launchAboutWindow(mynavgroup)
{
var window = TI.UI.createWindow({navgroup: mynavgroup});
}
Since it's always the same anyway, is would it be bad practice to simply have a GLOBAL navgroup? instead of passing navgroup contexts all over the place?
function launchAboutWindow()
{
var window = TI.UI.createWindow({navgroup: GLOBALNAVGROUP});
}
What would be the negative consequence of doing this?
Well, the thing about Global variables, is that they always remain in memory, and are not being cleaned by the garbage collector. That's why it's good practice to avoid putting stuff on the global scope.
Of course, to every rule there is an exception. In my opinion, since you are passing the reference around anyhow, you are keeping that object (and it's reference) alive. In my opinion it would only make sense to keep that sort of thing global - even just to make your code more maintainable - which is not less important than keeping it right.
I wouldn't do that for every object - each case on it's own.
BTW - in Alloy, there is a special namespace Titanium has for global object (don't abuse it either!).
Also, I recommend looking at the cross platform navigation controller:
http://www.appcelerator.com/blog/2013/08/a-cross-platform-navigation-group/
http://www.appcelerator.com/blog/2011/08/forging-titanium-episode-2-a-cross-platform-navigation-controller/
https://github.com/vuinguyen/Ti-Navigation-Controller
I have been looking at Swift the last couple of days and it looks good, I am confused on a point though even after review of the guide Apple published.
I understand that memory management in Swift uses ARC and therefore quite safe, however in situations where I'd like more fine grained control of object creation and destruction I'm slightly confused.
Take these two simple examples:
In ObjC, when you'd (for example) like a UIViewController you intend to push to stay around for the lifetime of your app and only be created once (say because it's expensive to create), you'd make it a property and then wrap it's Alloc Init like this:
if (!self.myProperty)
{
self.myProperty = [[MyObj alloc] init];
}
This way it only creates once, keeps state and the same object instance can be reused indefinitely, yielding known object references, and good performance not having to recreate each time the viewController goes out of scope.
Another example of this is with NSMutableString, vs Swifts String.
You can set an NSMutableString's string many times and it will keep the same object reference, however this doesn't seem to be the case with a Swift String when changing it.
This may just be 'deprecated thinking in Objective C' but is there a best practice with regard to keeping around expensive objects or to keep state (same object) for certain objects during the lifetime of their usage.
I think what you're looking for is a "Lazy stored property":
The example in the swift docs is:
class DataManager {
#lazy var importer = DataImporter()
var data = String[]()
// the DataManager class would provide data management functionality here
}
and it says about #lazy:
A lazy stored property is a property whose initial value is not calculated until the first time it is used. You indicate a lazy stored property by writing the #lazy attribute before its declaration.
The comment about NSMutableString is a little different, you can continue to use NSMutableString in your swift code, but Apple are encouraging developers to use the Swift built-in types whenever possible :)
see the "String Mutability" section in the swift docs
As the title says: is it necessary or is it good practice to check if an object is nil before allocating and initializing it, like so?
if (!_menuFetcher) {
_menuFetcher = [[MenuFetcher alloc] init];
_menuFetcher.delegate = self;
}
I presume this is called lazy loading right?
Lazy loading is deferring the creation of an object until you need it. UIViewController does that with its view -- creating a view hierarchy takes a good deal of time and memory, so it isn't done until the view is accessed. You might implement lazy loading using a conditional statement like the one you've shown. Ideally, you'd put that in the accessor for a property and use the property everywhere so you don't have conditionals all over the place.
It's not bad practice to check whether an object exists, but using properties lets you limit the number of places where you need to do so, which simplifies the rest of your code.
It's not necessary to always check whether something exists before allocating -- most of the time you should already know. For example, in your -init method you know that nothing has been allocated yet, and you can create whatever objects your new object will need.
Yes, this basically prevents re-initialising something that was already initialised. You should always do this, unless you are 100% sure that you are not re-initialising. However, this is not called Lazy Loading - that something different.
Lazy loading is used, for example, when loading images in a table view. Instead of loading all of the images that are in the table view, you only load these ones that are visible on screen. This approach is better for loading times, performance and memory.
Usually you do something like this in a getter method.
Say you have a property:
#property (nonatomic, strong) MenuFetcher *menuFetcher;
That you use in a view controller say, then you can implement a getter for the property as so:
- (MenuFetcher *)menuFetcher
{
if (!_menuFetcher) {
_menuFetcher = [[MenuFetcher alloc] init];
_menuFetcher.delegate = self;
}
return _menuFetcher;
}
You're correct that this is a form of lazy initialisation (rather than loading) - the memory is not allocated nor the instance initialised until the point it is required. Also, you don't need to have a centralised initialisation routine, nor do you need to worry about the object being set up yet - if it doesn't exist it gets created - if you blow it away by setting it back to nil, the next time something wants an instance of that type, it gets created again. So it is somewhat elegant and efficient in that regard.
So, in effect, the first time you try to read the property by calling self.menuFetcher, if it hasn't been setup yet, your getter will notice it is nil and create and initialise it for you. Once set, the property is no longer nil, so the getter just returns the object as held.
Use lazy initialisation if that is appropriate for what you are writing. The only thing to really watch out for is getters that do other things beyond the lazy initialisation - getters that have 'side effects' can be a pain if things go wrong. Keep them simple and focused.
It depends.
If you just need to instanciate a new object then there is no need to check for nil.
There is no need to check for nil before allocating an object. Not at all.
However, this is some quite good and widely spread pattern where you check for nil before acutally using the object. If the reference is nil then you create it 'on the fly'. You have to see it in the context of the following code (either here or in the caller). There _menuFetcher will be used in some way. (most probabyl at least.)
Or this is in a method that may be called multiple times like viewWillAppear in a view controller. You may have good reasons not to instanciate and initilize the object earlier and wnat to instanicate it only once. On the next call of that very method you would simply reuse the object that was created earlier.
(I meant to write this in a comment first, not an answer. But it became to long for a comment.)
Lazy loading means 'loading on demand', you perform operation only when it is really needed, it something like:
- (MenuFetcher *)instance{
if (_menuFetcher == nil){
_menuFetcher = [[MenuFetcher alloc] init];
}
return _menuFetcher;
}