title: TableView with SearchController - DEINIT not called
body: <p>I have added a search bar and search display controller from the interface builder to my app. I am not able to get it to deinit (dealloc) properly.</p>
  
  <p>It is showing the following behavior (swift2, ios9):</p>
  
  <ul>
  <li>User doesn't search anything, just selects an item from tableView, <strong>DEINIT is called</strong></li>
  <li>User searches something (or just taps in the search bar), cancel the search, select item from tableView, <strong>DEINIT is called</strong></li>
  <li>User searches something (or just taps the search bar), and selects an item from tableView, <strong>DEINIT is NOT called</strong> :(</li>
  </ul>
  
  <p>The same behaviour occurs if I select "Back" in the navigation controller instead of selecting an item.</p>
  
  <pre><code>code removed - refer to COMPLETE CODE at bottom of post.
  </code></pre>
  
  <p>Any help appreciated!</p>
  
  <p><strong>UPDATE</strong> Further testing has shown that removing the progressHud/loadingHud from the view controller entirely has no impact on the DEINIT not getting called. It must be something to do with the tableview or the searchcontroller itself...</p>
  
  <p><strong>UPDATE 2</strong> I have tried calling the searchBarCancelButtonClicked() method in my viewWillDissapear and it still doesn't release. Even though if you click "cancel" and then navigate away it does...</p>
  
  <p><strong>UPDATE 3</strong> Changing the willDisappear/didDisappear to the following has had no impact on the DEINIT - but does not give buggy interface issues (thanks Polina). I am trying to nil out anything I can to get a release, but no luck so far.</p>
  
  <pre><code>    override func viewWillDisappear(animated: Bool) {
      super.viewWillDisappear(true)
      searchBarCancelButtonClicked(searchController.searchBar)
  }
  
  override func viewDidDisappear(animated: Bool) {
      print("View did disappear")
      searchController.searchBar.resignFirstResponder()
      searchController.searchBar.endEditing(true)
      searchController.active = false
      loadingHud.removeFromSuperview()
      progressHud.removeFromSuperview()
      searchController.searchBar.delegate = nil
      searchController.searchResultsUpdater = nil
      searchController = nil
      tableView = nil
  
      super.viewDidDisappear(true)
  
  }
  </code></pre>
  
  <p><strong>UPDATE 4</strong> Still no answer I've found. Really hoping someone can help!</p>
  
  <p><strong>UPDATE 5</strong> In response to @ConfusedByCode - I have updated the following methods to use <code>[unowned self] in</code> inside of all closure or background thread operations:</p>
  
  <pre><code>code removed - refer to COMPLETE CODE at bottom of post
  </code></pre>
  
  <p>I am still not seeing DEINIT. I'm checking to make sure I didn't make a silly mistake somewhere.</p>
  
  <p><strong>UPDATE 6</strong> I have removed extra weak self's and made sure the closures are utilizing <code>[weak self] in</code> and unwrapping them safely. The DEINIT still is not called.</p>
  
  <p><strong>UPDATE 7</strong> Changed two things to no avail - made appDel <code>unowned let appDel</code>, and put searchBar.resignFirstResponder() in finishSearch(). Still not receiving deinit.</p>
  
  <p><strong>COMPLETE CODE: (REPRESENTS UPDATE 7)</strong></p>
  
  <p><strong>FOR CORRECT ANSWER SEE CODE PASTED UNDER CORRECTED CODE</strong></p>
  
  <pre><code>class AirportSearchTBVC: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
  var airportData = [Dictionary&lt;String, String&gt;]()
  var filteredData = [Dictionary&lt;String, String&gt;]()
  var searchController: UISearchController!
  unowned let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
  var progressHud: ProgressHUD!
  var loadingHud: ProgressHUD!
  var arrDepOfFlight: String!
  var dateOfFlight: NSDate!
  var tailNum: String!
  var selectedAirportIdent: String!
  
  deinit {
      print("TBVC Dealloc")
  
  }
  override func viewDidLoad() {
      super.viewDidLoad()
  }
  
  override func viewWillAppear(animated: Bool) {
      super.viewWillAppear(true)
  
      progressHud = ProgressHUD(text: "Searching")
      loadingHud = ProgressHUD(text: "Loading")
      searchController = UISearchController(searchResultsController: nil)
      searchController.searchResultsUpdater = self
      searchController.dimsBackgroundDuringPresentation = false
      searchController.searchBar.sizeToFit()
      tableView.tableHeaderView = searchController.searchBar
      definesPresentationContext = true
      searchController.hidesNavigationBarDuringPresentation = false
      searchController.searchBar.delegate = self
      view.addSubview(loadingHud)
  
      appDel.backgroundThread(background: { [weak self] in
          if let weakSelf = self {
              let airportHelper = AirportHelper()
              weakSelf.airportData = airportHelper.getAirportSearchData()
          }
          },
          completion: {
              dispatch_async(dispatch_get_main_queue()) { [weak self] in
                  if let weakSelf = self {
                      if weakSelf.isVisible &amp;&amp; weakSelf.isTopViewController {
                          weakSelf.filteredData = (weakSelf.airportData)
                          weakSelf.loadingHud.removeFromSuperview()
                          weakSelf.updateSearchResultsForSearchController(weakSelf.searchController)
                          weakSelf.tableView.reloadData()
  
                      }
  
                  }
              }
      });
  
  }
  
  //MARK: Searchbar methods (All background thread methods are in here)
  func searchBarCancelButtonClicked(searchBar: UISearchBar) {
      searchController.searchBar.endEditing(true)
      searchController.searchBar.resignFirstResponder()
      filteredData = airportData
      tableView.reloadData()
  }
  func searchBarSearchButtonClicked(searchBar: UISearchBar) {
      if isVisible &amp;&amp; isTopViewController {
          if let startCount = searchController.searchBar.text?.length {
              if searchController.searchBar.text!.length &gt;= 3 &amp;&amp; searchController.searchBar.text!.length == startCount{
                  view.addSubview(progressHud)
                  finishSearch()
              }
          }
      }
  }
  func finishSearch () {
      appDel.backgroundThread(background: { [weak self] in
          if let weakSelf = self {
              if weakSelf.isVisible &amp;&amp; weakSelf.isTopViewController {
                  let searchText = weakSelf.searchController.searchBar.text!.lowercaseString
  
                  weakSelf.searchController.searchBar.resignFirstResponder()
                  weakSelf.filteredData = weakSelf.airportData.filter{
                      if let ident = $0["ident"] {
                          if ident.lowercaseString.rangeOfString(searchText) != nil {
                              return true
                          }
                      }
                      if let name  = $0["name"] {
                          if name.lowercaseString.rangeOfString(searchText) != nil {
                              return true
                          }
                      }
                      if let city = $0["municipality"] {
                          if city.lowercaseString.rangeOfString(searchText) != nil {
                              return true
                          }
                      }
                      return false
                  }
              }
          }
          },
          completion: {
              dispatch_async(dispatch_get_main_queue()) { [weak self] in
  
                  if let weakSelf = self {
                      if weakSelf.isVisible &amp;&amp; weakSelf.isTopViewController {
                          weakSelf.tableView.reloadData()
                          weakSelf.progressHud.removeFromSuperview()
                      }
                  }
              }
      });
  }
  
  func updateSearchResultsForSearchController(searchController: UISearchController) {
      if isVisible &amp;&amp; isTopViewController {
          if let startCount = searchController.searchBar.text?.length {
              if searchController.searchBar.text!.length &gt;= 3 &amp;&amp; searchController.searchBar.text!.length == startCount{
                  view.addSubview(progressHud)
                  finishSearch()
              }
          }
      }
  }
  
  
  //MARK: Table view methods:
  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      // Dispose of any resources that can be recreated.
  }
  
  override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -&gt; CGFloat {
      return 72
  }
  override func numberOfSectionsInTableView(tableView: UITableView) -&gt; Int {
      // #warning Incomplete implementation, return the number of sections
      return 1
  }
  
  override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
      // #warning Incomplete implementation, return the number of rows
  
  
  
      if searchController.active {
          return filteredData.count
      } else {
          return airportData.count
      }
  
  
  }
  
  override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -&gt; UITableViewCell {
  
      var cell: AirportSearchTableViewCell
      cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! AirportSearchTableViewCell
      if searchController.active {
          let airportDict = filteredData[indexPath.row]
          let airportIdent = airportDict["ident"]
          let airportName = airportDict["name"]
          let airportRegion = airportDict["iso_region"]
          let airportCity = airportDict["municipality"]
          cell.loadItem(airportIdent, name: airportName, region: airportRegion, city: airportCity)
      } else {
          let airportDict = airportData[indexPath.row]
          let airportIdent = airportDict["ident"]
          let airportName = airportDict["name"]
          let airportRegion = airportDict["iso_region"]
          let airportCity = airportDict["municipality"]
          cell.loadItem(airportIdent, name: airportName, region: airportRegion, city: airportCity)
      }
      return cell
  }
  override func viewWillDisappear(animated: Bool) {
      super.viewWillDisappear(true)
      searchBarCancelButtonClicked(searchController.searchBar)
  }
  
  override func viewDidDisappear(animated: Bool) {
      print("View did disappear")
      searchController.searchBar.resignFirstResponder()
      searchController.searchBar.endEditing(true)
      searchController.active = false
      searchController.delegate = nil
      searchController.resignFirstResponder()
      loadingHud.removeFromSuperview()
      progressHud.removeFromSuperview()
      searchController.searchBar.delegate = nil
      searchController.searchResultsUpdater = nil
      searchController.removeFromParentViewController()
      searchController = nil
      tableView = nil
      super.viewDidDisappear(true)
  
  }
  
  func delay(delay:Double, closure:()-&gt;()) {
      dispatch_after(
          dispatch_time(
              DISPATCH_TIME_NOW,
              Int64(delay * Double(NSEC_PER_SEC))
          ),
          dispatch_get_main_queue(), closure)
  }
  
  
  
  // MARK: - Navigation
  
  // In a storyboard-based application, you will often want to do a little preparation before navigation
  override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
      let cell = tableView.cellForRowAtIndexPath(indexPath) as! AirportSearchTableViewCell
      selectedAirportIdent = cell.identLbl.text!
      self.performSegueWithIdentifier("searchMapVC", sender: nil)
      tableView.deselectRowAtIndexPath(indexPath, animated: true)
  }
  override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
      // Get the new view controller using segue.destinationViewController.
      // Pass the selected object to the new view controller.
      if segue.identifier == "searchMapVC" {
          let mapVC = segue.destinationViewController as! SearchMapController
          mapVC.arrDepOfFlight = arrDepOfFlight
          mapVC.dateOfFlight = dateOfFlight
          mapVC.tailNum = tailNum
          mapVC.selectedAirportIdent = selectedAirportIdent
      }
  }
  }
  //MARK: EXTENSIONS
  extension String {
      var length: Int { return characters.count    }  // Swift 2.0
  }
  extension UIViewController {
      public var isVisible: Bool {
          if isViewLoaded() {
              return view.window != nil
          }
          return false
  }
  
  public var isTopViewController: Bool {
          if self.navigationController != nil {
              return self.navigationController?.visibleViewController === self
          } else if self.tabBarController != nil {
              return self.tabBarController?.selectedViewController == self &amp;&amp; self.presentedViewController == nil
          } else {
              return self.presentedViewController == nil &amp;&amp; self.isVisible
          }
      }
  
  }
  </code></pre>
  
  <p><strong>CORRECTED CODE [FIXED]</strong> As Mikael Hellman suggested, there is some sort of retain bug occurring with definesPresentationContext which was originally in my viewWillAppear method. I have removed that line and done some slight massaging of my code. It is now working perfectly.</p>
  
  <p>Thank you so much for the effort and the answer! Also, thank you to @confusedByCode for the help - I am sure his suggestions were a component of my problem as well, but didn't end up being the final answer.</p>
  
  <pre><code>import UIKit
  
  class AirportSearchTBVC: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
  var airportData = [Dictionary&lt;String, String&gt;]()
  var filteredData = [Dictionary&lt;String, String&gt;]()
  var searchController: UISearchController!
  let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
  var progressHud: ProgressHUD!
  var loadingHud: ProgressHUD!
  var arrDepOfFlight: String!
  var dateOfFlight: NSDate!
  var tailNum: String!
  var selectedAirportIdent: String!
  
  deinit {
      print("TBVC Dealloc")
  
  }
  override func viewDidLoad() {
      super.viewDidLoad()
  }
  
  override func viewWillAppear(animated: Bool) {
      super.viewWillAppear(true)
  
      progressHud = ProgressHUD(text: "Searching")
      loadingHud = ProgressHUD(text: "Loading")
      searchController = UISearchController(searchResultsController: nil)
      searchController.searchResultsUpdater = self
      searchController.dimsBackgroundDuringPresentation = false
      searchController.searchBar.sizeToFit()
      tableView.tableHeaderView = searchController.searchBar
      //definesPresentationContext = true
      searchController.hidesNavigationBarDuringPresentation = false
      searchController.searchBar.delegate = self
      view.addSubview(loadingHud)
  
      appDel.backgroundThread(background: { [weak self] in
          if let weakSelf = self {
              let airportHelper = AirportHelper()
              weakSelf.airportData = airportHelper.getAirportSearchData()
          }
          },
          completion: {
              dispatch_async(dispatch_get_main_queue()) { [weak self] in
                  if let weakSelf = self {
                      print("isVisible: \(weakSelf.isVisible)")
                      print("isTopViewController: \(weakSelf.isTopViewController)")
                      if weakSelf.isVisible  {
                          weakSelf.filteredData = (weakSelf.airportData)
                          weakSelf.loadingHud.removeFromSuperview()
                          weakSelf.updateSearchResultsForSearchController(weakSelf.searchController)
                          weakSelf.tableView.reloadData()
                      }
  
  
                  }
              }
      });
  
  }
  
  //MARK: Searchbar methods (All background thread methods are in here)
  func searchBarCancelButtonClicked(searchBar: UISearchBar) {
      searchController.searchBar.endEditing(true)
      searchController.searchBar.resignFirstResponder()
      filteredData = airportData
      tableView.reloadData()
  }
  func searchBarSearchButtonClicked(searchBar: UISearchBar) {
  
          if let startCount = searchController.searchBar.text?.length {
              if searchController.searchBar.text!.length &gt;= 3 &amp;&amp; searchController.searchBar.text!.length == startCount{
                  view.addSubview(progressHud)
                  finishSearch()
              }
          }
  
  }
  func finishSearch () {
      appDel.backgroundThread(background: { [weak self] in
          if let weakSelf = self {
  
                  let searchText = weakSelf.searchController.searchBar.text!.lowercaseString
  
                  //weakSelf.searchController.searchBar.resignFirstResponder()
                  weakSelf.filteredData = weakSelf.airportData.filter{
                      if let ident = $0["ident"] {
                          if ident.lowercaseString.rangeOfString(searchText) != nil {
                              return true
                          }
                      }
                      if let name  = $0["name"] {
                          if name.lowercaseString.rangeOfString(searchText) != nil {
                              return true
                          }
                      }
                      if let city = $0["municipality"] {
                          if city.lowercaseString.rangeOfString(searchText) != nil {
                              return true
                          }
                      }
                      return false
                  }
  
          }
          },
          completion: {
              dispatch_async(dispatch_get_main_queue()) { [unowned self] in
  
                  if self.isVisible {
  
                          self.tableView.reloadData()
                          self.progressHud.removeFromSuperview()
                  }
  
  
  
              }
      });
  }
  
  func updateSearchResultsForSearchController(searchController: UISearchController) {
  
          if let startCount = searchController.searchBar.text?.length {
              if searchController.searchBar.text!.length &gt;= 3 &amp;&amp; searchController.searchBar.text!.length == startCount{
                  view.addSubview(progressHud)
                  finishSearch()
              }
          }
  
  }
  
  
  //MARK: Table view methods:
  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      // Dispose of any resources that can be recreated.
  }
  
  override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -&gt; CGFloat {
      return 72
  }
  override func numberOfSectionsInTableView(tableView: UITableView) -&gt; Int {
      // #warning Incomplete implementation, return the number of sections
      return 1
  }
  
  override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
      // #warning Incomplete implementation, return the number of rows
  
  
  
      if searchController.active {
          return filteredData.count
      } else {
          return airportData.count
      }
  
  
  }
  
  override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -&gt; UITableViewCell {
  
      var cell: AirportSearchTableViewCell
      cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! AirportSearchTableViewCell
      if searchController.active {
          let airportDict = filteredData[indexPath.row]
          let airportIdent = airportDict["ident"]
          let airportName = airportDict["name"]
          let airportRegion = airportDict["iso_region"]
          let airportCity = airportDict["municipality"]
          cell.loadItem(airportIdent, name: airportName, region: airportRegion, city: airportCity)
      } else {
          let airportDict = airportData[indexPath.row]
          let airportIdent = airportDict["ident"]
          let airportName = airportDict["name"]
          let airportRegion = airportDict["iso_region"]
          let airportCity = airportDict["municipality"]
          cell.loadItem(airportIdent, name: airportName, region: airportRegion, city: airportCity)
      }
      return cell
  }
  override func viewWillDisappear(animated: Bool) {
      super.viewWillDisappear(true)
      searchController.active = false
      loadingHud.removeFromSuperview()
      progressHud.removeFromSuperview()
  }
  
  override func viewDidDisappear(animated: Bool) {
      print("View did disappear")
  
  
      super.viewDidDisappear(true)
  
  }
  
  func delay(delay:Double, closure:()-&gt;()) {
      dispatch_after(
          dispatch_time(
              DISPATCH_TIME_NOW,
              Int64(delay * Double(NSEC_PER_SEC))
          ),
          dispatch_get_main_queue(), closure)
  }
  
  
  
  // MARK: - Navigation
  
  // In a storyboard-based application, you will often want to do a little preparation before navigation
  override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
      let cell = tableView.cellForRowAtIndexPath(indexPath) as! AirportSearchTableViewCell
      selectedAirportIdent = cell.identLbl.text!
      self.performSegueWithIdentifier("searchMapVC", sender: nil)
      tableView.deselectRowAtIndexPath(indexPath, animated: true)
  }
  override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
      // Get the new view controller using segue.destinationViewController.
      // Pass the selected object to the new view controller.
      if segue.identifier == "searchMapVC" {
          let mapVC = segue.destinationViewController as! SearchMapController
          mapVC.arrDepOfFlight = arrDepOfFlight
          mapVC.dateOfFlight = dateOfFlight
          mapVC.tailNum = tailNum
          mapVC.selectedAirportIdent = selectedAirportIdent
      }
  }
  }
  //MARK: EXTENSIONS
  extension String {
      var length: Int { return characters.count    }  // Swift 2.0
  }
  extension UIViewController {
      public var isVisible: Bool {
          if isViewLoaded() {
              return view.window != nil
          }
          return false
  }
  
  public var isTopViewController: Bool {
          if self.navigationController != nil {
              return self.navigationController?.visibleViewController === self
          } else if self.tabBarController != nil {
              return self.tabBarController?.selectedViewController == self &amp;&amp; self.presentedViewController == nil
          } else {
              return self.presentedViewController == nil &amp;&amp; self.isVisible
          }
      }
  
  }
  </code></pre>