带有SearchController的TableView - 未调用DEINIT [英] TableView with SearchController - DEINIT not called

查看:130
本文介绍了带有SearchController的TableView - 未调用DEINIT的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已将界面构建器中的搜索栏和搜索显示控制器添加到我的应用程序中。我无法正确地进行deinit(dealloc)。

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.

它显示以下行为(swift2,ios9):

It is showing the following behavior (swift2, ios9):


  • 用户不搜索任何内容,只需从tableView中选择一个项目,调用DEINIT

  • 用户搜索一些东西(或只是点击搜索栏),取消搜索,从tableView中选择项目,调用DEINIT

  • 用户搜索内容(或只是点击搜索bar),并从tableView中选择一个项目,不调用DEINIT :(

  • User doesn't search anything, just selects an item from tableView, DEINIT is called
  • User searches something (or just taps in the search bar), cancel the search, select item from tableView, DEINIT is called
  • User searches something (or just taps the search bar), and selects an item from tableView, DEINIT is NOT called :(

发生相同的行为如果我在导航控制器中选择返回而不是选择项目。

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

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

任何帮助表示感谢!

更新进一步测试表明,从视图控制器中删除progressHud / loadingHud对DEINIT没有任何影响没有被召唤。它必须与tableview或searchcontroller本身有关...

UPDATE 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...

UPDATE 2 我试过调用searchBarCancelButtonClicked()方法我的viewWillDissapear,它仍然没有发布。即使你点击取消然后导航它也会... ...

UPDATE 2 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...

更新3 将willDisappear / didDisappear更改为以下内容对DEINIT没有影响 - 但是没有给出错误的界面问题(感谢Polina)。我试图找出我能发布的任何内容,但到目前为止没有运气。

UPDATE 3 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.

    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 我找不到答案。非常希望有人可以提供帮助!

UPDATE 4 Still no answer I've found. Really hoping someone can help!

更新5 为了响应@ConfusedByCode - 我更新了以下方法以使用所有闭包或后台线程操作中的中的[unowned self]:

UPDATE 5 In response to @ConfusedByCode - I have updated the following methods to use [unowned self] in inside of all closure or background thread operations:

code removed - refer to COMPLETE CODE at bottom of post

我仍然没有看到DEINIT。我正在检查以确保我没有在某个地方犯一个愚蠢的错误。

I am still not seeing DEINIT. I'm checking to make sure I didn't make a silly mistake somewhere.

更新6 我删除了额外的弱自我并确保关闭是利用 [弱自我] 并安全地展开它们。 DEINIT仍未被调用。

UPDATE 6 I have removed extra weak self's and made sure the closures are utilizing [weak self] in and unwrapping them safely. The DEINIT still is not called.

更新7 更改了两件事无济于事 - appDel 无主让appDel ,并将searchBar.resignFirstResponder()放在finishSearch()中。仍然没有收到deinit。

UPDATE 7 Changed two things to no avail - made appDel unowned let appDel, and put searchBar.resignFirstResponder() in finishSearch(). Still not receiving 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
        }
    }

}

已修正的代码[已修复] 正如Mikael Hellman所说,使用definePresentationContext时会出现某种遗留错误,最初是在我的viewWillAppear方法中。我已删除该行并对我的代码进行了一些轻微的按摩。它现在运作得很好。

CORRECTED CODE [FIXED] 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.

非常感谢您的努力和答案!另外,感谢@confusedByCode的帮助 - 我相信他的建议也是我问题的一个组成部分,但最终并没有成为最终答案。

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.

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
        }
    }

}


推荐答案

删除了我的旧答案,发现了问题。

Deleted my old answer, found the problem.

删除:

definesPresentationContext = true // Remove this line...

在此处阅读:

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

UISearchController中必定存在错误。某些Apple代码必须保留您的控制器。

There must a bug in UISearchController. Some Apple code must be retaining your controller.

当控制器取消链接时,它可能没有完成转换视图(动画)(它具有特殊的toView和fromView等,我们无法访问),这可以防止控制器被解除分配。

Maybe the transition view (animation) is not completed when the controller is unlinked (it has special toView and fromView etc, that we can not reach), and this prevents the controller from being deallocated.

我认为你应该将此作为Apple的错误提交。

I think you should file this as a bug with Apple.

另外我建议将你的deinit更改为:

Also I would recommend changing your deinit to:

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

这将确保搜索控制器在取消分配时永远不会尝试呈现内容,因为这会产生警告和潜在的意外行为。

This will ensure the search controller is never trying to present stuff when getting deallocated, as this creates warnings and potential unexpected behavior.

这篇关于带有SearchController的TableView - 未调用DEINIT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆