TableView with SearchController - DEINIT is not called

I added a search bar and a search display controller from the interface designer to my application. I cannot get it to deinoc correctly.

The following behavior is shown (swift2, ios9):

  • The user is not looking for anything, just selects an item from tableView, DEINIT is called
  • The user is looking for something (or just brief in the search bar), cancel the search, select an item from the View table, DEINIT is called
  • The user searches for something (or simply selects the search string), and selects an element from tableView, DEINIT is NOT called : (

The same behavior occurs if I select "Back" in the navigation controller instead of selecting an item.

code removed - refer to COMPLETE CODE at bottom of post.

Any help appreciated!

UPDATE . Further testing showed that removing progressHud / loadHud from the view controller does not completely affect the DEINIT failure caused. This should be somehow related to the table view or the search controller itself ...

UPDATE 2 I tried calling the searchBarCancelButtonClicked () method in my WillDissapear view, and it is still not free. Despite the fact that if you click cancel, then release it, ...

UPDATE 3 Changing the willDisappear / didDisappear parameter to the following did not affect DEINIT - but it does not give an error problem (thanks to Pauline). I'm trying to do my best to get a release, but so far no luck.

    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)

}

UPDATE 4 I did not find an answer. Really hoping someone can help!

5 @ConfusedByCode - [unowned self] in :

code removed - refer to COMPLETE CODE at bottom of post

DEINIT. , -.

6 ​​ , [weak self] in . DEINIT .

7 appDel unowned let appDel searchBar.resignFirstResponder() finishSearch(). deinit.

: ( 7)

,

class AirportSearchTBVC: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
var airportData = [Dictionary<String, String>]()
var filteredData = [Dictionary<String, String>]()
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 && 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 && isTopViewController {
        if let startCount = searchController.searchBar.text?.length {
            if searchController.searchBar.text!.length >= 3 && searchController.searchBar.text!.length == startCount{
                view.addSubview(progressHud)
                finishSearch()
            }
        }
    }
}
func finishSearch () {
    appDel.backgroundThread(background: { [weak self] in
        if let weakSelf = self {
            if weakSelf.isVisible && 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 && weakSelf.isTopViewController {
                        weakSelf.tableView.reloadData()
                        weakSelf.progressHud.removeFromSuperview()
                    }
                }
            }
    });
}

func updateSearchResultsForSearchController(searchController: UISearchController) {
    if isVisible && isTopViewController {
        if let startCount = searchController.searchBar.text?.length {
            if searchController.searchBar.text!.length >= 3 && 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) -> CGFloat {
    return 72
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> 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) -> 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:()->()) {
    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 && self.presentedViewController == nil
        } else {
            return self.presentedViewController == nil && self.isVisible
        }
    }

}

[]. , , definePresentationContext, viewWillAppear. . .

! , @confusedByCode - , , .

import UIKit

class AirportSearchTBVC: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
var airportData = [Dictionary<String, String>]()
var filteredData = [Dictionary<String, String>]()
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 >= 3 && 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 >= 3 && 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) -> CGFloat {
    return 72
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> 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) -> 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:()->()) {
    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 && self.presentedViewController == nil
        } else {
            return self.presentedViewController == nil && self.isVisible
        }
    }

}
+4
3

, .

definesPresentationContext = true // Remove this line...

:

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instp/UIViewController/definesPresentationContext


UISearchController . Apple .

, () , ( toView fromView .., ), .

, Apple.


deinit :

deinit {
  print("TBVC Dealloc")
  if let superView = searchController.view.superview
  {
    superView.removeFromSuperview()
  }
}

, , .

+2

, , ,

  override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    searchController?.dismissViewControllerAnimated(false, completion: nil)
  }

Dropbox https://www.dropbox.com/s/zzs0m4n9maxd2u5/TestSearch.zip?dl=0

+2

, , "", .

, :

if let safeSelf = weakSelf {
    appDel.backgroundThread(background: {
            let airportHelper = AirportHelper()
            safeSelf.airportData = airportHelper.getAirportSearchData()
        },
        completion: {
            dispatch_async(dispatch_get_main_queue()) {
                if safeSelf.isVisible && safeSelf.isTopViewController {
                    safeSelf.filteredData = (safeSelf.airportData)
                    safeSelf.loadingHud.removeFromSuperview()
                    safeSelf.tableView.reloadData()
                }
            }
    });
}

:

appDel.backgroundThread(background: { [weak self] in
        let airportHelper = AirportHelper()
        self?.airportData = airportHelper.getAirportSearchData()
    },
    completion: {
        dispatch_async(dispatch_get_main_queue()) { [weak self] in
            if self?.isVisible && safeSelf.isTopViewController {
                self?.filteredData = (safeSelf.airportData)
                self?.loadingHud.removeFromSuperview()
                self?.tableView.reloadData()
            }
        }
});

[unowned self], , , , . , weak self unowned self , , .

0
source

Source: https://habr.com/ru/post/1614200/


All Articles