-4

Edit: Please read carefully. Everyone who has tried to help thinks I am asking about why a nil unwrapped optional causes a crash. I am not. I am trying to determine why it was nil in the first place. And thanks to those who have tried to help! I do appreciate it.

Newbie question. I am building a weather forecast app as part of learning to program in Swift.

I have a protocol for passing a location back to my main forecast class to use to return a forecast for a certain location. I have two different classed that use the protocol when returning a location to return a forecast for.

I was having an issue that one of the classes would cause a crash for unwrapping a nil with regard to delegate to the forecast class. The other class, set up with exactly the same return function did not have this error. I tracked the crash down to the prepare(for segue: function in the forecast class. Because I did not need t pass information forward to one of the classes, I did not set destinationViewController.delegate = self for the class that crashed. My question is simply why did I have to set that in the forecast class in order to prevent the unwrapping a nil crash.

I know I missed something in why a protocol has to be set up this way, but I can't seem to find the answer as to why. I wanted to ask this question for me, and for others to find when this crash occurs.

I think this post addresses my issue, but does not answer the "why," just the "what." What does setting the destinationVC.delegate = self in the class that initiates the segue do that setting weak var delegate: ForecastTableViewController! does not?

Thanks.

My code is as follows:

ForecastTableViewController class:

class ForecastTableViewController: UITableViewController, CLLocationManagerDelegate, LocationToForecast {

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    getLocationNameAndStoreLocation(latitude: (self.weatherData.locationCoordinates.latitude), longitude: (self.weatherData.locationCoordinates.longitude))

    if segue.identifier == "locationViewSegue", let destinationViewController = segue.destination as? LocationViewController  {
        destinationViewController.delegate = self
        destinationViewController.weatherData = self.weatherData
        destinationViewController.wxObservationStationsArray = self.wxObservationStationsArray
        destinationViewController.newCoordinates = self.weatherData.locationCoordinates
    } else if segue.identifier == "searchPriorLocations", let destinationViewController = segue.destination as? SearchBarTableViewController  {
        destinationViewController.delegate = self
    }
}

LocationViewController class:

class LocationViewController: UIViewController, CLLocationManagerDelegate, UIGestureRecognizerDelegate, UIPickerViewDataSource, UIPickerViewDelegate {

    weak var delegate: ForecastTableViewController!


   override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(true)

    switch coordinateChoice {
    case currentLocationTrue: newCoordinates = currentLocation
    case UIPickerTrue:

        newCoordinates = UIPickerLocation

    case longPressTrue: newCoordinates = longPressLocation
    default: break
    }

    self.delegate!.returnLocationToForecast(locationToReturn: newCoordinates)
    performSegueToReturnBack()
}

SearchBarTableViewController class:

class SearchBarTableViewController: UITableViewController, UISearchResultsUpdating, NSFetchedResultsControllerDelegate {

    weak var delegate: ForecastTableViewController!


   override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(true)

    if !(newCoordinates.latitude == 0 && newCoordinates.longitude == 0) {
        self.delegate!.returnLocationToForecast(locationToReturn: newCoordinates)
    }

    performSegueToReturnBack()
}
Community
  • 1
  • 1
Yrb
  • 8,103
  • 2
  • 14
  • 44
  • See the information on unwrapping optionals - http://stackoverflow.com/documentation/swift/247/optionals/913/unwrapping-an-optional#t=201701220215478858772. Whenever you use ! You tell the compiler that the variable must have a value and if it doesn't you will get a crash. You should use ? To conditionally unwrap your value if it can be nil – Paulw11 Jan 22 '17 at 02:18
  • I didn't ask the why part well. I do understand that if you unwrap an optional and it is nil, it crashes. My question is more to the why was it nil in this instance. The view I am segueing from is pushed onto the stack when the new view comes up, so it still exists, yet the delegate is nil. Why? – Yrb Jan 22 '17 at 04:10
  • Because unless you set `destinationViewController.delegate = something` (such as `self`) it will be nil. Then, you say `self.delegate!` so it will crash – Paulw11 Jan 22 '17 at 04:20
  • I am not confused by optional. I get that "!" forces an unwrap where "?" allows nil. I get that you have to use "!" to unwrap a value to use it. My question is not about optionals. It is about why a prior view controller reference is nil when you just segued from t and it was pushed onto the view stack. Why does a reference to it become nil if you do not set the delegate of the view controller you just segued to, to the view controller you just segued from? Without setting that in prepareToSegue, when you try to use a reference to the prior vc it crashes. – Yrb Jan 22 '17 at 14:31
  • Have you read the documentation on optionals in the Swift book and that I linked to? You don't *have* to use !; this is a force unwrap and will crash if it is nil. There is no magic that sets the `delegate` value; you need to do it. `delegate` is a instance property just like any other. You could call the property `fred` if you wanted `delegate` is just a convention. If you don't give it a value, it will be nil. It doesn't make sense to declare a property be both weak and implicitly unwrapped. Your property should be `weak var delegate: ForecastTableViewController?` – Paulw11 Jan 22 '17 at 20:20
  • Thank you for the response, but I am really NOT asking about optionals. I am asking why it is nil in the first place, whether I call it delegate, or fred or foo. My question is why it was nil when unwrapped, not the consequences of unwrapping a nil. My question is a bit deeper. I am trying to figure out why the reference to the prior VC was nil without setting the destinationVC.delegate before the segue. – Yrb Jan 23 '17 at 01:31
  • `delegate` will be `nil` if you don't assign it a value, simple as that. You keep talking about the "reference to the prior VC", but the only variable you are accessing is `delegate`. If you don't set it, it wont have a value. – Paulw11 Jan 23 '17 at 01:36
  • So why does "weak var delegate: ForecastTableViewController!" not assign a value to delegate? In other words, why doesn't "ForecastTableViewController" exist at the time of assignment? – Yrb Jan 24 '17 at 13:07
  • Because that is just a declaration. There is no assignment operator (`=`) in that line. It simply says that there is a property, `delegate` which is of type `ForecastTableViewController!`, and it is weak. It is an implicitly unwrapped optional, which means that it does not need to be initialised in the classes Initializer (it is an optional) but you do not need to unwrap it (with ? Or !) when referring to it. You keep saying that this isn't about optional but it is all about optionals. You are force unwrapping an optional that is nil. – Paulw11 Jan 24 '17 at 20:36
  • Thank you, and that makes perfect sense. That is what I was missing in this whole thing. I was not actually assigning a value, and therefore it was nil. And yes, I understand that if I unwrap a nil I get a crash. That is not what confused me. As a follow up, why does setting the delegate of the current VC as the prior VC prevent the nil condition? Also, if you put you prior comment and the answer to this question, I can accept it as the answer to my question. – Yrb Jan 28 '17 at 16:15
  • You are effectively asking "why does setting the delegate to a value make it non-nil?", to which the answer is obvious. – Paulw11 Jan 28 '17 at 20:48
  • So you are saying that is the SAME delegate I am accessing when I set "weak var delegate: ForecastTableViewController!"? If so, why? That is what I am missing. – Yrb Jan 29 '17 at 00:54
  • As I have aid multiple times, that line doesn't create or set a delegate, it merely declares a property that can hold a reference to a delegate. If I said `var i: Int` what is the value of `i`? Nothing until a value is assigned. – Paulw11 Jan 29 '17 at 00:56
  • I really am not sure why you are so combative over this. All I am asking is when I put in code " weak var delegate: ForecastTableViewController!" am I accessing the delegate that was set when I wrote "destinationViewController.delegate = self" in ForecastTableViewController? It reads to me that I am setting the delegate for the SearchbarTableViewController class as that is what the segue that destinationViewController refers to at that point. Obviously they are related, but I don't know how. – Yrb Feb 04 '17 at 01:30
  • I am not being combative. When you write `weak var delegate: ForecastViewController!` you are declaring a property that can be assigned a value. When you say `self.delegate.someThing()` you are accessing the property and when you say `destinationViewController.delegate = self` you are assigning a value to the property. If you don't do this last step then it has no value and is nil. Since you have declared it as implicitly unwrapped you get a crash if you access it without setting a value. – Paulw11 Feb 04 '17 at 01:38
  • Your delegate property should be declared as `weak var delegate: ForecastViewController?` and you should unwrap it before you use it. Declaring it as both weak and implicitly unwrapped is a particularly bad idea as a weak property can become nil at any point if the object it refers to is released. – Paulw11 Feb 04 '17 at 01:43
  • Unless of course if it becomes nil, you want the crash anyway. So simply, to make sure I understand, `weak var delegate: ForecastTableViewController` whether implicitly unwrapped or not, **is** the delegate that I am setting when I assign `destinationViewController.delegate = self` where the destinationViewController is SearchBarTableViewController where this particular `weak var delegate: ForecastTableViewController` is written, correct? – Yrb Feb 04 '17 at 16:17

2 Answers2

4

My question is simply why did I have to set that in the forecast class in order to prevent the unwrapping a nil crash

Because you said self.delegate!. That means "crash me if I have no delegate." You cannot complain if Swift does exactly what you said to do!

If you don't want to crash, then don't say that. Say self.delegate? instead.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • What Matt said. Think of ! as the "crash if nil" operator, and avoid it until you are more experienced in Swift. – Duncan C Jan 22 '17 at 02:29
  • 2
    Sometimes you really _do_ want to crash if nil. (Because nil would mean there is a fatal bug in the program.) But this is evidently not one of those situations, since you (the OP) are surprised that you are crashing. – matt Jan 22 '17 at 03:10
0

try with AnyObject instead of class . in some cases it may crash if we use class in non class object .

protocol DemoDelegate : AnyObject {   
}

class DemoViewController: UIViewController 
{

weak var delegate : DemoDelegate?

}
Fisky
  • 114
  • 5