CALayercornerRadius 导致应用程序间歇性崩溃

Posted

技术标签:

【中文标题】CALayercornerRadius 导致应用程序间歇性崩溃【英文标题】:CALayer cornerRadius causing app to crash intermittently 【发布时间】:2020-07-17 10:58:12 【问题描述】:

请帮助解决这个错误。这让我困惑了几天。该错误仅在少数选定设备上发生,但我无法在我的设备上模拟该行为。

我有一个向用户显示通知的应用。通知导航控制器有一个标签,它使用下面的类来设置一些属性

class LabelPaddingNotifications: UILabel 
var insets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)


func padding(_ top: CGFloat, _ bottom: CGFloat, _ left: CGFloat, _ right: CGFloat) 
    self.frame = CGRect(x: 0, y: 0, width: self.frame.width + left + right, height: self.frame.height + top + bottom)
    insets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)


override func drawText(in rect: CGRect) 
    super.drawText(in: rect.inset(by: insets))


override var intrinsicContentSize: CGSize 
    get 
        var contentSize = super.intrinsicContentSize
        contentSize.height += insets.top + insets.bottom + 50
        contentSize.width += insets.left + insets.right
        return contentSize
    

应用程序有时会在以下行崩溃。不过,我无法在我的设备或模拟器上模拟行为。以下代码在类的viewDidLoad中

labelNotification.layer.cornerRadius = _smallButtonCornerRadius

出口定义为

@IBOutlet var labelNotification: LabelPadding!

小半径在常量文件中定义如下

let _smallButtonCornerRadius : CGFloat = 7

我还有一个CALayer的扩展如下

extension CALayer 

func shake(duration: TimeInterval = TimeInterval(0.5)) 

    let animationKey = "shake"
    removeAnimation(forKey: animationKey)

    let kAnimation = CAKeyframeAnimation(keyPath: "transform.translation.x")
    kAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    kAnimation.duration = duration

    var needOffset = frame.width * 0.15,
    values = [CGFloat]()

    let minOffset = needOffset * 0.1

    repeat 

        values.append(-needOffset)
        values.append(needOffset)
        needOffset *= 0.5
     while needOffset > minOffset

    values.append(0)
    kAnimation.values = values
    add(kAnimation, forKey: animationKey)


func pulsate() 
    let pulse = CASpringAnimation(keyPath: "transform.scale")
    pulse.duration = 0.5
    pulse.fromValue = 0.95
    pulse.toValue = 1.0
    pulse.autoreverses = false
    pulse.repeatCount = 0
    pulse.initialVelocity = 0.5
    pulse.damping = 1.0
    add(pulse, forKey: nil)


Crashlytics 向我显示以下错误

堆栈跟踪表明堆损坏可能导致您的应用崩溃。释放悬空指针、线程竞争或错误的指针算法很容易发生内存损坏。要记住的重要一点是,由此产生的崩溃可能会在最初的损坏之后很久才发生。因此,此崩溃的堆栈跟踪可能无法提供代码中错误位置的任何线索。但是,您仍然可以使用 Apple 的工具修复内存问题。为了快速解决内存损坏问题,我们建议使用 Xcode 的内存调试工具定期审核您的应用:Visual Memory Debugger、Zombies Instrument、Address Sanitizer、Thread Sanitizer 和 malloc 诊断。

虽然 XCode 管理器中的崩溃在上面的行中向我显示了一个错误,但给了我以下信息

更新

在收到我在应用程序中设置的本地通知后,这个特定的视图控制器会从 AppDelegate 文件中触发。它的代码如下

DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: 
                if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "VCShowNotification") as? ShowNotificationVC 
                if let window = self.window, let rootViewController = window.rootViewController 
                    var currentController = rootViewController
                    while let presentedController = currentController.presentedViewController 
                        currentController = presentedController
                    
                    let navController = UINavigationController(rootViewController: controller)
                    currentController.present(navController, animated: true, completion: nil)
                    //currentController.present(controller, animated: true, completion: nil)
                
            

我现在更新了从我的 appdelegate 启动通知控制器的方法,方法是在主线程上添加 1 秒的延迟。现在这会打开应用程序,但通知控制器不再出现在某些设备上。该应用程序已停止崩溃。我无法在我的设备上模拟错误,这就是为什么我真的在努力解决这个问题。

【问题讨论】:

您找到问题了吗?如果有,那是什么? 我没找到大卫。我仍在为错误而苦苦挣扎。我还尝试运行内存泄漏测试,但没有任何结果。 此时解决此问题的最佳方法是让您花时间创建一个单独的项目,其中包含此内容,并通过某种方式对其进行修改,因此它会不时崩溃到时间。将其放在 Dropbox 或同等设备上。有了赏金,您肯定会很快得到答案,也许来自我!没有人可以根据您的描述解决它(写得很好!)。如果我的建议没有帮助,不知道还能说什么。 谢谢,大卫。我看看我能不能做到这一点。我是一个人的军队,有太多的工作要同时处理。 我遇到了类似的问题来显示警报。您需要一个新的 Window 对象 - 请参阅此链接:***.com/a/58295128/1633251 【参考方案1】:

此类错误通常是由于线程不匹配造成的,也就是说,此 UI 代码在主线程中运行,但其他代码在辅助线程中更改其参数。

您可以添加一个观察者并对此进行测试:

// Note typing this in Stack so no way to see typos etc
@IBOutlet var labelNotification: LabelPadding! 
  didSet 
    assert(Thread.isMainThread)
    assert(LabelPadding != nil)
  

在设置之前读取该属性的可能性很小,因此您可能需要将其重新定义为:

@IBOutlet var labelNotification: LabelPadding?

然后在每次使用中使用一个守卫:

guard let labelNotification = labelNotification else  return 
    

【讨论】:

谢谢大卫。我会试一试,看看会发生什么。 @user2672052 我觉得大卫的直觉是正确的,这是由于线程问题。如果我没记错的话,通知是在它发布的线程/队列上传递的。你能保证你在“更新”中发布的代码在主线程上运行吗? 请查看我的问题中的更新代码。我已经更新了 appdelegate 中的代码,应用停止了崩溃,它打开了,但从未触发视图控制器,这仍然只影响了大约 9-10% 的设备。

以上是关于CALayercornerRadius 导致应用程序间歇性崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 协程协程异常处理 ④ ( Android 协程中出现异常导致应用崩溃 | Android 协程中使用协程异常处理器捕获异常 | Android 全局异常处理器 )

Golang 哪些情况会导致协程泄露?

Golang 哪些情况会导致协程泄露?

x64 汇编中的递归阶乘子例程导致堆栈溢出

嵌入式/X86下linux系统死机及内存优化

更改应用程序权限会导致再次调用 oncreate()