由于未捕获的exception’NSRangeException’而终止应用程序,原因是:’无法删除观察者 – ios
我正在使用swift开发ios app。 我使用的是xcode7.0.1。 使用TableViewController。 当我点击行时我想扩展并在我再次点击它时折叠。 我正在关注gitHub的教程。 现在我Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer
面临Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer
的错误Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer
for the key path "frame" from because it is not registered as an observer.'
Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer
for the key path "frame" from because it is not registered as an observer.'
Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer
for the key path "frame" from because it is not registered as an observer.'
我希望下面的代码行会导致问题。
我的UITableViewCell类代码:
func checkHeight(){ expandaple_view.hidden = (frame.size.height < expandTips.expandedHeight) } func WatchFrameChanges() { addObserver(self , forKeyPath: "frame", options: .New, context: nil ) checkHeight() } func ignoreFrameChanges(){ removeObserver(self, forKeyPath: "frame") } override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { if keyPath == "frame"{ checkHeight() } }
在我的TableViewController代码中:
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { (cell as! expandTips) . WatchFrameChanges() } override func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { (cell as! expandTips) . ignoreFrameChanges() } override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { if (indexPath == selectedIndexPath ){ return expandTips.expandedHeight }else { return expandTips.defaultHeight } }
我是ios的新手。 我希望这一定是个简单的问题。 请有人帮我解决这个问题。
我不知道有什么细节需要在这里发布。 如果我想添加更多细节,请发表评论。
从@Leandros的评论中我找到了解决方案。 只需使用布尔变量跟踪观察者。
使用以下代码,我找到了解决方案:
var frameAdded = false func checkHeight(){ expanding_view.hidden = (frame.size.height < expandTips.expandedHeight) } func WatchFrameChanges() { if(!frameAdded){ addObserver(self , forKeyPath: "frame", options: .New, context: nil ) frameAdded = true } } func ignoreFrameChanges() { if(frameAdded){ removeObserver(self, forKeyPath: "frame") frameAdded = false } } override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { if keyPath == "frame"{ checkHeight() } } deinit { print("deinit called"); ignoreFrameChanges() }
如果没有注册观察者,则删除KVO观察者会抛出exception。
不幸的是,没有办法检查观察者是否已注册。 众所周知的解决方法是捕获这样的exception(Objective-C):
@try { [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(isFinished))]; } @catch (NSException * __unused exception) {}
要在Swift中执行此操作,您必须使用Swift 2.1,因为它缺少对 try {} catch {}
。你可以在2.1中看到它是如何工作的。
更正:虽然Swift 2引入了自己的error handling约定,但是使用do/try/catch
关键字,这些并不意味着它们与Objective-C中的相同,并且仍然存在(从Swift 2.1和Xcode 7.1开始)没有办法在Swift中处理从系统库抛出的NSExceptions,而不是编写Objective-C代码到@catch
它们,然后从Swift调用该代码。
你可以在这里阅读更多关于KVO的内容,其中还有一个关于安全取消订阅的部分,其中提到了这个不幸的设计。