Swift performance on optional UI elements - swift

Setting up a UILabel in Swift I can do the following
#IBOutlet var wLabel: UILabel!
or
#IBOutlet var wLabel: UILabel?
Setting the text I can either do
wLabel.text = "..."
or, for the latter do
wLabel?.text = "..." // with a question mark for the optional
Keeping them optional will help for the case when they are unexpectedly nil as it will just skip this call then and go ahead with the code. Having them declared with the ! would crash the app if the label was nil.
Now why would you not make everything optional? The only reason I could think of would be for better performance. Should I stay away from having them optional if my app has a lot of UI elements so them being optional would mean a disadvantage regarding performance? I wasn't able to find any information on this anyhere.

Using ? in your Outlet will make your code untracable
Crashes are very good in some scenarios, because if no crash is there then it becomes super difficult to trace even small error.
Consider the examples of #IBOutlet with !:
#IBOutlet var wLabel: UILabel!
wLabel.isEnabled = true
Just remove the connection of your Outlet label from your storyboard and run the app, your app will crash on wLabel.isEnabled = true. Since you got a crash so you can got to your storyboard see whether the connection is proper or not. In case there is no connect you add it and damn! you solved the problem easily.
Now consider the examples of #IBOutlet with ?:
#IBOutlet var wLabel: UILabel?
wLabel?.isEnabled = true
Do the same thing, just remove the connection of your Outlet label from your storyboard and run the app, your app won't crash at all. You won't be able to know the error, hence making your code a mess and untracable.
Apple guys were very aware of optional thing, they did force unwrapping of #IBOutlet for a reason. And of course there is no performance difference between ! and ?

If you're setting something up as an outlet it's set up as a force unwrapped optional (with exclamation mark !) because Swift requires your classes to have all of their properties initialized when the object is constructed (i.e. init method is called). Outlets are not created during object construction but rather assigned later when the xib or storyboard data is loaded into your component. Force unwrapped optionals are basically a way to say that you guarantee that the property will be initialized after object's init is called and before they're used.
There are no performance gains to using either of these. An Optional? type means that the property can either be nil or have a value. A force unwrapped optional ! means that the property is guaranteed to have a value when it is used. Upholding this is up to the developer and using such property if its value is nil will result in a crash.

Related

Non optional IBOutlet returns Fatal error: when referenced from another UIViewController class

I have a UIView defined in
class InteractViewController: UIViewController {
#IBOutlet weak var tipsView: UIView!
var tipslayer: CALayer {
return tipsView.layer
}
}
There are a number of constraints also attached in the class as well as labels for example.
#IBOutlet weak var tips1Top: NSLayoutConstraint!
#IBOutlet weak var tips1Right: NSLayoutConstraint!
#IBOutlet weak var tipsTitle: UILabel!
#IBOutlet weak var tipsDesc: UILabel!
All are connected on the storyboard to their correct outlets. Referencing these within the actual InteractViewController itself I am able to adjust opacity, update labels on the layer etc.
However, these are not needed unless the user is first using the application so a simple if else on the InteractViewController calls a function to either display and update properties of these or not show them at all.
The function that controls and updates these properties is in another class (Swift file in the project called.
class TipsViewController: UIViewController {
func tips() {
InteractViewController().tipslayer.transform = CATransform3DMakeScale(0.8, 0.8, 1.0)
}
}
Causes the app to crash creating
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
on
return tipsView.layer (the InteractViewController)
tipsView and tipslayer are of cause not optionals and are not declared as optionals.
I have three questions I hope I can get assistance with.
Is having the physical reference to the outlets on the ViewController class InteractViewController: UIViewController the problem? If so is it because they are supposed to be programmatically added instead on the class TipsViewController: UIViewController (which does not have a physical visual view controller in Xcode)?
Does having a physical reference to the outlets on the
class InteractViewController: UIViewController
create unnecessary memory use if on InteractViewController the call to the function on the second ViewContoller TipsViewController.tips() is never called so in other words the answer to question 1 is essentially any physical outlets should be programmatically added on lass TipsViewController: UIViewController?
If there is no memory issue and knowing that tipsView and the returning tipslayer are not optionals why am I simply unable to reference them from the tips() function in the other class (TipsViewController: UIViewController)? I have seen multiple posts about "passing data between view controllers" but I am not passing data between variables or arrays. I want/ need to simply update label string, constraints (objects in one view controller class from another view controller class.
I know people are busy so just to say in advance I appreciate all feedback and assistance with the problem.
It was suggested that my question was a duplicate of [What does "fatal error: unexpectedly found nil while unwrapping an Optional value" mean?
But this is not my question. MY question relates specifically to accessing an Outlet from another ViewController which is then generating a Nil response to the Outlet because as explained InteractViewController is apparently not referencing the ViewController. Please read the explanations and answer provided by Rob which answers this specific question.
Your title says "IBOutlet returns Fatal error: when referenced from another UIViewController class". The bottom line is that one view controller should never access another view controller's outlets.
In your code snippet, you employ the InteractViewController() syntax. Unfortunately, this just instantiates view controller, but does not hook up outlets in storyboard. You need to instantiate via storyboard and, even then, you can’t reference your outlets immediately, but only after viewDidLoad is called later. Only the view controller itself should be trying to access its own outlets, anyway.
The TipsViewController() or InteractViewController() syntax says "create a new, blank instance of that view controller". It's equivalent to saying TipsViewController.init() or InteractViewController.init(). And, worse, it's not using the storyboard at all (and thus no outlets will be hooked up). That's not what you want. You should avoid using this ViewController() syntax, as outlets will never been accessible that way.
InteractViewController should determine whether TipsViewController should be presented (e.g. checking user defaults), and if so, present it. The only thing InteractViewController should do, though, is:
figure out whether to present TipsViewController;
if so, present that TipsViewController; and
pass model data (e.g. whatever InteractViewController retrieved from UserDefaults) to the TipsViewController instance that is created for you.
But anything and everything about how TipsViewController presents its subviews, is the purview of TipsViewController alone. InteractViewController should never interact directly with any outlets of TipsViewController.

SceneKit crashes when I use constraint and minimize App

I have very strange behavior in my app, that I can't explain. I have a SceneKit session running, and some nodes have constraints assigned (no matter what kind). When app goes to background I'm removing all nodes from the scene, and when it appears again I'm adding them again (This is needed for reasons not needed here). When I have this constraints assigned and resume app - it crashes. Not always, but very often. I have no idea what to do to fix this. I tried removing constraints when removing nodes, but this did not help. The error I get is in a screenshot. Can someone help me find a solution?
Your question doesn't show the code that you are referencing, but I can tell you that an EXC_BAD_ACCESS error typically means you are trying to access an object that has been released from memory.
Without going in depth about strong vs. weak variables, you likely have a weak or unowned variable (to that constraint) that is becoming nil and that you are trying to access (by force-unwrapping).
When you are removing these nodes, or when the view disappears, the constraint is released from memory. So the fix here is to either make the reference to this constraint strong or to make a new constraint, keep a strong reference to that in the class, and assign that constraint to the object.
Assuming your reference is to a storyboard, replace:
#IBOutlet weak var constraint: NSLayoutConstraint!
With:
#IBOutlet var constraint: NSLayoutConstraint!
Also note that the variable is probable force-unwrapped (NSLayoutConstraint!), which is likely why you are running into the EXC_BAD_ACCESS error. Set your constraint to be a strong variable instead of weak, or better yet, avoid force-unwrapping optionals if possible!
Note: If this doesn't solve your problem, leave a comment after adding more details to your question.

UITextField always results in nil

I'm trying to get the information of a UITextField and then reuse that data from the UITextField with swift 2.0 and Xcode 7. But every time I run the app, the app crashes and says:
fatal error: unexpectedly found nil while unwrapping an Optional value
even though there is something in the textfield.
Someone knows how to solve this problem?
This is how my code looks:
#IBOutlet weak var entryAmount: UITextField! //TextField declaration
#IBAction func saveNewButtonTapped(sender: UIBarButtonItem) {
//update overall amount
let amount: String? = self.entryAmount.text! //Throws the error at this point
self.viewController.currentValue += amount
}
In another function, I wrote the same thing ("let amount: String? = entryAmount.text!") and when I printed "amount", it showed the right result. But as soon as I tried to use it further, "amount" was always set to nil, even tough it always printed the right result in the beginning.
I encounter the same thing with DatePicker, Switches and updating Button texts.
Someone knows something to help me? - Thanks
Btw I'm pretty new to swift and Xcode.
Update: Could it be because the TextField is in a ContainerView?
Update Two: I'm pretty much sure the problem is because of the ContainerView. I tried a TextField that isn't in a containerView and didn't get the error, it worked perfectly fine.
Please check in your Interface Builder that the UITextField is properly linked to your "entryAmount" IBOutlet. It might be the case that they are not properly connected and that's why the value is nil at runtime.
I could be wrong but what it seems like from your question is you're having trouble with scoping. Any variables you declare in a function are scoped only to that function.
Declaring amount in saveNewButtonTapped means amount is only set in that function. Using amount in another function will result in amount being nil. If you want to use variables between functions you need to declare them outside of a function.
In the example I have the var runningAmount. In the example, if I press the button that calls pressAnotherButton, I get the same fatal error you get because I'm unwrapping self.runningAmount before it's been set. But if I press the button that calls saveNewButtonTapped, even with nothing in the textfield, then press the button that calls pressAnotherButton, I get the output in the photo.

Why does Swift insist on this being an optional?

Caveat: I've been coding in Swift for about 2 days!
I am following along with some simple tutorials to build a status menu application. The following is a reduction of my AppDelegate.swift class:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var statusMenu: NSMenu!
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-2)
func applicationDidFinishLaunching(aNotification: NSNotification) {
let icon = NSImage(named: "StatusBarImage")
icon?.setTemplate(true)
}
}
My question is, why does Xcode insist that my icon constant be an optional (it was also OK with me forcing it to be unwrapped). The tutorial didn't show that the ? was needed, but Xcode shows me the following error if I leave it out:
Value of optional type 'NSImage' not unwrapped; did you mean to use '!' or '?'?
(I'm using Xcode 6.4, if that makes a difference.)
From Document:
Declaration:
init?(named name: String) -> NSImage
Which means by default it's initialiser is an optional.
Return Value
The NSImage object associated with the specified name or nil if no
such image was found.
Discussion
The NSImage class may cache a reference to the returned image object
for performance in some cases. However, the class holds onto cached
objects only while the object exists. If all strong references to the
image are subsequently removed, the object may be quietly removed from
the cache. Thus, if you plan to hold onto a returned image object, you
must maintain a strong reference to it like you would any Cocoa
object. You can clear an image object from the cache explicitly by
calling the object’s setName: method and specifying nil for the image
name.
That's why it is asking you did you mean to use '!' or '?'
So if you use ! that means you are unwrapping optional value and if you use ? compiler will consider it as optional.
Because image named StatusBarImage might as well not be in the assets and it would result in bad access exception.
Actually Xcode is not forcing anything : if you check the NSImage documentation you will see that the NSImage initializer is a failable initializer.
Like #FruitAddict said the whole point of those initializers is to fail gracefully (by returning an optional with a nil value) when the object could not be created. Check Apple Developer Blog
By adding ! or this ? You know explicitly that imageNamed can returned you a nil.
And Compiler just makes sure that you know about it.

Swift, two issues. 1) weak var 2) bang operator for #IBOutlet

Per:
#IBOutlet weak var nameLabel: UILabel!
Whenever I declare my IBOutlets, i just use var instead of weak var. But I've recently come across a few code templates that use weak var. Why do they do it? What's the added benefit?
Why is there a bang operator at the end of UILabel. I know it's required and i go along w/ it, but just asking it now.
Thanks in advance.
Swift IBOutlet are weak by default (but others properties are strong by default). So both writing are the same.
You have more details about the difference between weak and strong here
According to apple documentation
When you declare an outlet in Swift, you should make the type of the
outlet an implicitly unwrapped optional (!). This way, you can let the
storyboard connect the outlets at runtime, after initialization.
The outlets are weak since the view elements are owned (strongly) by the view. I think it's technically OK for your view controller to have a strong reference too, but not necessary.
Weak variables are optional since they can be nil. You can declare your outlets with ? instead but that means using force-unwrapping or optional binding every time. Declaring them as implicitly-unwrapped optionals with ! is just a convenience.
You use weak when referring to IBOutlets because so long as the object remains in its superview there will be a strong reference to it. See weak or strong for IBOutlets.
Next, the bang operator indicates that the IBOutlet is an explicitly unwrapped label. With the bang operator, it guarantees that the object will exist, so when referencing it, you can simply reference it like this:
someLabel.text = "some text"
However, you can make them IBOutlets optional:
#IBOutlet weak var someLabel: UILabel?
But you must use ? when accessing them
someLabel?.text = "some text"
#gregheo's response is the best explained, to further elaborate: if you consider ownership in this situation the View object referred to by the #IBOutlet usually shouldn't be owned by the View Controller referring to it.
Rather it should be owned by its superview wherever it may be in the tree (which happens strongly by UIView.subviews). The View Controller instead owns the very root of it's View Tree strongly (UIViewController.view). weak explicitly declares a non-owning reference that may become nil at different points in the View Controller's life.
Here I would offer an alternative to using !: using an implicitly unwrapped optional reference is a dangerous practice that weakens the tools Swift offers us. A View Controller which is loaded from Xib or Storyboard includes in its lifetime a regular time after being created and before it's View has been loaded where the #IBOutlet reference is nil, every time. To assume no one will operate on the member during that time means not using the Swift grammar and compiler's feedback to our advantage.
Additionally #IBOutlets are a powerful tool that enables a flexible, visually focused approach when designing a screen or view. It is common practice to have your View Controller expose #IBOutlets for all information it has available, regardless of whether or not it is known that it will be used and separately deciding which to actually connect and use when building and iterating on the View from within Interface Builder.
Additionally-additionally if your View should be flexible enough to be instantiated from Xib/Storyboard AND from code, depending on how you decide referenced subviews should be instantiated and connected they may or may not be available immediately.
For the reasons above I define mine: #IBOutlet weak var nameLabel: UILabel?