Cha*_*lie 6 uitableview retain ios swift uisearchcontroller
我在界面构建器中添加了一个搜索栏和搜索显示控制器到我的应用程序.我无法正确地进行deinit(dealloc).
它显示以下行为(swift2,ios9):
如果我在导航控制器中选择"返回"而不是选择项目,则会出现相同的行为.
code removed - refer to COMPLETE CODE at bottom of post.
Run Code Online (Sandbox Code Playgroud)
任何帮助赞赏!
更新进一步测试表明,从视图控制器中删除progressHud/loadingHud完全没有影响DEINIT没有被调用.它必须与tableview或searchcontroller本身有关...
更新2我尝试在viewWillDissapear中调用searchBarCancelButtonClicked()方法,但仍然没有释放.即使你点击"取消"然后导航它就会......
更新3将willDisappear/didDisappear更改为以下对DEINIT没有影响 - 但不会给出错误的界面问题(感谢Polina).我试图找出我能发布的任何东西,但到目前为止还没有运气.
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)
}
Run Code Online (Sandbox Code Playgroud)
更新4我找不到答案.真的希望有人可以帮忙!
更新5响应@ConfusedByCode - 我更新了以下方法,以便[unowned self] in在所有闭包或后台线程操作中使用:
code removed - refer to COMPLETE CODE at bottom of post
Run Code Online (Sandbox Code Playgroud)
我仍然没有看到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
}
}
}
Run Code Online (Sandbox Code Playgroud)
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.
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
删除了我的旧答案,发现了问题.
去掉:
definesPresentationContext = true // Remove this line...
Run Code Online (Sandbox Code Playgroud)
在这里阅读:
UISearchController中必定存在错误.某些Apple代码必须保留您的控制器.
当控制器取消链接时,它可能没有完成转换视图(动画)(它具有特殊的toView和fromView等,我们无法访问),这可以防止控制器被释放.
我认为你应该将此作为Apple的错误提交.
另外我建议将你的deinit改为:
deinit {
print("TBVC Dealloc")
if let superView = searchController.view.superview
{
superView.removeFromSuperview()
}
}
Run Code Online (Sandbox Code Playgroud)
这将确保搜索控制器在取消分配时永远不会尝试呈现内容,因为这会产生警告和潜在的意外行为.
我今天也遇到了这个问题,这行代码似乎可以让你的课程发布
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
searchController?.dismissViewControllerAnimated(false, completion: nil)
}
Run Code Online (Sandbox Code Playgroud)
这是我在 dropbox 上的示例项目 https://www.dropbox.com/s/zzs0m4n9maxd2u5/TestSearch.zip?dl=0
| 归档时间: |
|
| 查看次数: |
2023 次 |
| 最近记录: |