【问题标题】:NSAnimationContext crashing when called by an ObserverNSAnimationContext 在被观察者调用时崩溃
【发布时间】:2020-07-07 17:01:25
【问题描述】:

我有两个 WebView:webViewcustomizerWebView。这两个 WKWebViews 都由一个尾随约束附加。本质上,当我进入菜单并单击“显示定制器”showCustomizer() 或“隐藏定制器”hideCustomizer() 时,它会调用相应的函数并显示或隐藏与 customizerWebView 相关的所有内容。

为了澄清,当从附加的NSMenuItems 调用这些函数时,一切都按预期工作和动画。 然而,show/hideCustomizer() 被从本质上检测到 URL 的观察者调用时 - 即。 url.contains("#close") - 应用程序在 animator() 代码的第一行崩溃,错误为:Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

ViewController.swift

import Cocoa
import WebKit

class ViewController: NSViewController, WKUIDelegate, WKNavigationDelegate {
    var customizerURLObserver: NSKeyValueObservation?

    @IBOutlet var webView: WKWebView!
    @IBOutlet var customizerWebView: WKWebView!
    @IBOutlet var rightConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad
        ...
        customizerURLObserver = customizerWebView.observe(\.url, options: .new) { webView, change in
            let url = "\(String(describing: change.newValue))"
            ViewController().urlDidChange(urlString: url) }
    }

    func urlDidChange(urlString: String) {
        let url = cleanURL(urlString)
        if url.contains("#close") { hideCustomizer() }  // Observer call to hide function
    }

    @IBAction func showCustomizerMenu(_ sender: Any) { showCustomizer() }  // These work flawlessly
    @IBAction func hideCustomizerMenu(_ sender: Any) { hideCustomizer() }  // These work flawlessly

    func showCustomizer() {
        let customTimeFunction = CAMediaTimingFunction(controlPoints: 5/6, 0.2, 2/6, 0.9)
        NSAnimationContext.runAnimationGroup({(_ context: NSAnimationContext) -> Void in
            context.timingFunction = customTimeFunction
            context.duration = 0.3
            rightConstraint.animator().constant = 280
            customizerWebView.animator().isHidden = false
            webView.animator().alphaValue = 0.6
        }, completionHandler: {() -> Void in
        })
    }

    func hideCustomizer() {
        let customTimeFunction = CAMediaTimingFunction(controlPoints: 5/6, 0.2, 2/6, 0.9)
        NSAnimationContext.runAnimationGroup({(_ context: NSAnimationContext) -> Void in
            context.timingFunction = customTimeFunction
            context.duration = 0.3
            webView.animator().alphaValue = 1     // Found nil crash highlights this line
            rightConstraint.animator().constant = 0
        }, completionHandler: {() -> Void in
            self.customizerWebView.isHidden = true
        })
    }
}

有人能告诉我为什么这个动画在从 NSMenu 调用时看起来和工作完美无瑕 100 次,但当 hideCustomizer() 从 Observer 函数调用一次时崩溃?

我也尝试过调用NSMenu对象函数hideCustomizerMenu(self),但无济于事。

【问题讨论】:

    标签: swift macos core-graphics core-animation nsanimationcontext


    【解决方案1】:

    上线:

    ViewController().urlDidChange(urlString: url)
    

    您错误地创建了视图控制器类的新实例并在 那个 实例上调用 urlDidChange。由于这个新实例不是从故事板/xib 创建的,它的所有出口都是 nil,因此当您尝试在 hideCustomizer 中调用其 webView 上的 animator 方法时,它会崩溃,因为它是 nil。

    相反,在self 上调用urlDidChange(实际上是一个弱化的self,这样您就不会创建保留循环):

    customizerURLObserver = customizerWebView.observe(\.url, options: .new) { [weak self] webView, change in
        let url = "\(String(describing: change.newValue))"
        self?.urlDidChange(urlString: url)
    }
    

    【讨论】:

    • 现在我觉得自己很愚蠢。非常感谢您提供更正的代码解释!现在我知道以后不要再犯同样的错误了。如果您有时间,只需快速跟进:为什么在此类情况下选择weak self?在编写新项目或功能时,我如何知道何时选择weak self
    • 没有问题!在这种特殊情况下,我知道使用[weak self],因为self 拥有对customizerURLObserver 的强引用,它拥有对observe(_:options:) 调用中指定的更改处理程序闭包的强引用,它拥有强引用到self,如果我没有让self变弱——所以会有一个从self->customizerURLObserver->closure->self开始的保留周期,这会造成内存泄漏,因为这些东西都不会释放。不过,一般来说,您几乎只需要了解哪些内容对哪些内容具有强引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-01
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多