weak reference in ios9 and ios8 - ios

[Problem soluted!Just want to know why there is such a difference in ios8 and ios9] I was making a register view controller these days and face with some problem about weak reference.
and below is some part of the code(swift)
problem come when I use an iphone6 ios8.1
it crashed. And then I noticed that the weak reference is not proper here. But the code runs well in my ios9 iphone6s. I ran this code on an iphone6 ios8 simulator, app crashed. So I think there is some thing different in processing weak reference in ios8 and ios9, But who can explain why..?
class VC: UIViewController {
weak var verifyTextField: UITextField?
override func viewdidload() {
//....
verifyTextField = newTextField();
view.addSubview(verifyTextField!);
}
func newTextField() -> UITextField {
let ntf = UITextField();
//do some settings to ntf;
return ntf;
}
}

You set your new UITextField instance to the weak var verifyTextField but before you add it as a subview (which increments the retain count) it is deallocated (the count is 0 since the var is weak) so verifyTextField! crashes, the crash you're getting is most likely the famous
Unexpectedly found nil while unwrapping an Optional
It's easy to fix it
Don't use a weak var
Don't force unwrap (use if let instead)
The code should be as follows:
class VC: UIViewController {
var verifyTextField: UITextField? //should not be weak
override func viewdidload() {
//....
verifyTextField = newTextField()
if let verifyTextField = verifyTextField {
view.addSubview(verifyTextField!)
}
}
func newTextField() -> UITextField {
let ntf = UITextField()
//do some settings to ntf
return ntf
}
}

Looks like your object is deallocated instantly after initialization because you don't store any strong reference for it.
Try this code:
override func viewdidload() {
//....
let verifyTextField = newTextField();
view.addSubview(verifyTextField);
self.verifyTextField = verifyTextField;
}
Also no need to use weak reference here, because verifyTextField doesn't have reference to your VC, so you won't get a retain cycle.

Related

UIView to UIViewController communication via protocol not working

maybe I am missing something really fundamental here, but after staring at the code for an hour or so, my brain is going trough cycles and I would appreciate a fresh glance at this problem.
I have the following UIView:
import UIKit
protocol DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool)
}
class locationXIBController: UIView {
#IBOutlet weak var loationLabel: UILabel!
#IBOutlet weak var vsedniOteviraciDobaLabel: UILabel!
#IBOutlet weak var prijmajiKartyLabel: UILabel!
#IBOutlet weak var detailViewButtonOutlet: UIButton!
#IBOutlet weak var backgroundViewButton: UIButton!
let openedBool = true
var detailViewWillShowUpDelegate: DetailViewWillShowUpDelegate?
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func vecerkaDetailButtonPressed(_ sender: UIButton) {
detailViewWillShowUpDelegate?.sendDetailOpened(openedBool)
print("pressed")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = detailViewButtonOutlet.hitTest(convert(point, to: detailViewButtonOutlet), with: event) {
return result
}
return backgroundViewButton.hitTest(convert(point, to: backgroundViewButton), with: event)
}
}
Now the problem is, that when I call/press the vecerkaDetailButtonPressed function I get "pressed" output in the console but the protocol for some reason doesn't go trough.
The other side looks like this (stripped for simplicity):
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
let locationXIB = locationXIBController()
let isVecerkaDetailOpened = false
override func viewDidLoad() {
locationXIB.detailViewWillShowUpDelegate = self
}
extension MapViewController: DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool) {
isVecerkaDetailOpened = openedBool
print("success")
}
}
I know the protocol value at the moment of execution is nil. As I said, any help is appreciated, thanks!
First, a couple of naming convention issues:
The name locationXIBController is a bad choice for a UIView object. It is a view object, not a controller object.
Second, class names in Swift should start with an upper-case letter. So LocationXIBView would be a much better name for that view class.
Next, your code
let locationXIB = locationXIBController()
...is wrong. That creates a brand-new instance of your locationXIBController class that you never install in your view hierarchy. You should make that line an IBOutlet:
#IBOutlet weak var locationXIB: locationXIBController!
And then you should control-drag from the locationXIBController in your StoryBoard onto the outlet in your view controller. That will cause Interface Builder to connect the outlet.
Now when you run your program the variable locationXIB will be connected to the locationXIBController view from your storyboard/XIB when it's loaded.
In addition to the answer of #Duncan C, you might check whether you need super.viewDidLoad() at the top of the viewDidLoad() method in the MapViewController class? Not doing that can lead to quirky things in your app.
I asked:
So does detailViewWillShowUpDelegate actually point at anything, or is it nil?
And you replied:
I just tried debugging and it is actually nil
So that's the problem... you need to set detailViewWillShowUpDelegate to point to a valid delegate object. This is often done in the .xib file or storyboard, and sometimes people forget to make that connection, so check there if it makes sense. Else you'll just need to get a reference to the delegate at some point before the code in question can run and set it up.
Answer to the credit of #Paulw11
I finally managed to get it working by communicating like so:
step 1) 1:1 communication via protocol between MKAnnotation and MKAnnotationView
step 2) 1:1 communication via protocol between MKAnnotationView and MapViewController passing the same data
Finally works like a charm, thanks!

Why would a UIAlert cause one of my other variables to be nil?

I'm trying to make a UIAlert pop up, but whenever the code calls the function that should do that, a value totally unrelated in the process caused an error saying that it returned nil when it expected to have a value. I solved the issue, but I have no idea why my solution worked.
The original:
var fullFreqHeard: [Double] = []
#IBOutlet weak var fullFreqLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
fullFreqLabel.text = "\(fullFreqHeard)"
}
The solution:
var fullFreqHeard: [Double] = []
#IBOutlet weak var fullFreqLabel: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
fullFreqLabel?.text = "\(fullFreqHeard)"
}
The difference is in the original I force unwrapped the label, whereas in the solution I used a '?'. What is going on here? I was also wondering if it might have anything to do with the alert.
I don't think it has anything to do with the alert. The reason force unwrapping fixes the error is because fullFreqLabel is initially nil. You have to initialize it before setting it's value. For instance, var fullFreqLabel = UILabel(frame: CGRect.zero)

Text Field and Label Editing Errors

Before I get started, I have to say that this project marks the first in-depth use of Swift and XCode in my life. I started it about a week ago (and am honestly impressed with how far I've gotten). I do not know too much about what I'm doing but I'm willing to learn.
Now, onto my question.
I'm trying to get a text field from one view controller to change a label from another. I thought I did it right, but it kept throwing syntax errors and such at me. After fixing that, I would run the code and get a SIGABRT error. Here's my code.
Here's the label, under BasicViewController (this isn't all that's in BasicViewController, I just cut out what I thought was pertinent)
class BasicViewController: UIViewController {
#IBOutlet weak var NameField: UILabel!
var NameText = String()
override func viewDidLoad() {
super.viewDidLoad()
NameField.text = NameText
}
And here's the text field, under EditCharController. This is where the SIGABRT error happens. (also, same thing with the lack of code.)
class EditCharController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var NameTextField: UITextField!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var NameTextDest : BasicViewController = segue.destinationViewController as! BasicViewController //Specifically, this is the line that it happens at.
NameTextDest.NameText = String(NameTextField.text)
}
}
Currently, XCode is telling me to change the var label to the let label, but even if I do that, it spits out this error.
Could not cast value of type 'UITabBarController'(?!?) (0x10310c8b0) to 'Project.BasicViewController' (0x1019a0060).
Last I checked, I didn't reference the UITabBarController anywhere in the code. Why am I getting this message?
Also any suggestions as to good in-depth tutorials will be very much appreciated.
Try
let index = 0 // change this to the tab index of the BasicViewController.
let NameTextDest : BasicViewController = (segue.destinationViewController as! UITabBarController).viewControllers[index] as! BasicViewController
NameTextDest.NameText = NameTextField.text!

Couldn't fix the error: unexpectedly found nil while unwrapping an Optional value

I couldn't fix that. When it tries to assign answeredCorrectly to numberLabel, I get a runtime error. :(
var answeredCorrectly: Int? = 0
func endGame(){
let lastViewLet = LastView()
println("Count of correct answers: \(answeredCorrectly)")
lastViewLet.numberLabel.text = String(stringInterpolationSegment: answeredCorrectly)
answeredCorrectly = 0
performSegueWithIdentifier("toEnd", sender: nil)
}
LastView class:
import Foundation
import UIKit
class LastView: ViewController {
#IBOutlet weak var numberLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
numberLabel.text = "1"
}
}
Your answeredCorrectly var is given a type of optional int, yet you assign a value of 0 to it. Are you sure that's what you want? If it will always have a value, you could use Int.
If you are using an optional, you should use the if let construct to ensure you have a value:
if let answeredCorrectlyInt = answeredCorrectly as? Int {
println("Count of correct answers: \(answeredCorrectlyInt)")
lastViewLet.numberLabel.text = String(answeredCorrectlyInt)
}
Also, you are using String(stringInterpolationSegment: answeredCorrectly) when you can just use String(answeredCorrectly) and it will do the right thing.
This can happen when the IBOutlet is not connected to actual UILabel in Interface Builder. There should be a little circle left from the #IBOutlet weak var numberLabel: UILabel!. If the circle is empty, the variable is not connected to the label. Make sure it is connected or try reconnecting it.
Also, are you actually showing the LastView view controller?

WKInterfaceTable & setNumberOfRows get crash "unexpectedly found nil while unwrapping an Optional value"

I'm working with WatchKit, I have a simple interface with only a table but I get an error with just a few lines of code, maybe I forgot something really basic.
My interface:
the row inside the table has the identifier:
and the custom class:
The controller is implemented by this code:
import WatchKit
import Foundation
class ActiveListController: WKInterfaceController
{
#IBOutlet weak var tableView: WKInterfaceTable!
override func awakeWithContext(context: AnyObject?)
{
super.awakeWithContext(context)
loadData()
}
override func willActivate()
{
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate()
{
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
func loadData()
{
tableView.setNumberOfRows(10, withRowType: "ItemRow") // GET ERROR
for index in 0...9
{
let row = tableView.rowControllerAtIndex(index) as! ItemRow
row.nameLabel.setText("test")
}
}
}
and obviously I have my custom class for the single row
import Foundation
import WatchKit
class ItemRow : NSObject
{
#IBOutlet weak var checkImage: WKInterfaceImage!
#IBOutlet weak var nameLabel: WKInterfaceLabel!
}
So when I run the app I get error when I try to set the number of rows but really I can't understand what is nil:
fatal error: unexpectedly found nil while unwrapping an Optional value
Maybe it's a simple mistake, maybe not but please help me :\
The for loop in your code is going from 1...10 and it should be 0...9 because the rows are 0 based
finally I found my mistake.
I forgot to set my only one interface for Apple Watch as Initial Controller.
Yep, unbelievable and embarrassing but that's it. The error that provides Xcode it's not the best , it would be better something like "initial controller missing".
I hope my question & answer can help someone one day :)

Resources