swift - UILabel 如何成为 CALayer 并导致崩溃?

标签 swift memory-management ios9

我有一个自定义 UIView,其中在循环内创建了多个实例:

let models = CDMyModel.MR_findAllSortedBy("position", ascending: true) as! [CDMyModel]

for view in myViews {
    view.removeFromSuperview()
}

self.myViews.removeAll(keepCapacity: true)

for model in models {
    let myView = MYFaqView(width: CGRectGetWidth(self.view.frame))
    myView.titleLabel.text = model.title
    myView.content.text = model.content
    myView.titleLabel.sizeToFit()
    self.scrollView.addSubview(myView)
    self.myViews.append(myView)
}

我有时确实会在 Crashlytics 中看到 myView.content.text = model.content 行中的崩溃:

enter image description here

根据崩溃,我认为它与内存有关,但我真的不知道此时 myView 是如何被释放的。

所有这些都发生在viewWillAppear:中。之前的删除是否与此有关?但我假设一切都发生在主线程上,所以这也不应该成为问题 - 我真的被困在这里了。

崩溃发生在 iOS 9 上。

编辑

MyFaqView初始化方法:

init(width:CGFloat) {
    self.width = width

    super.init(frame: CGRectZero)

    self.addSubview(self.titleLabel)
    self.addSubview(self.toggleImageView)
    self.addSubview(self.separatorView)
    self.content.clipsToBounds = true
    self.addSubview(self.content)

    self.translatesAutoresizingMaskIntoConstraints = false
    self.clipsToBounds = true
}

编辑

let content:UILabel = {
    let l = UILabel()
    l.numberOfLines = 0
    if let font = UIFont(name: "OpenSans", size: 14) {
        l.font = font
    }
    return l
}()

最佳答案

这些问题总是很难追踪。

基本上,发生的事情是内存损坏。之前被 UILabel 内容 占用的地址 0x14f822a0 已被其他内容使用,在本例中为 CALayer。如果崩溃发生在本地,您可以通过在 lldb 中输入 po 0x14f822a0 来验证这一点,并且肯定会将该地址输出为 CALayer 类型。

对于这些错误,虽然崩溃线可以提供线索,但它并不总是错误的原因。其他地方已经发生了一些事情。

虽然 Swift 主要是内存管理的,但对于粗心的人来说仍然存在陷阱。就我个人而言,我发现内存损坏的两个主要原因。第一个是由自引用闭包引起的保留循环。第二个 - 与您的问题更相关 - 是与 Storyboard 和 Xib 相关的 View 。

如果我们从逻辑上遵循这一点,我们可以认为CALayer现在占据了之前由您的UILabel内容占用的地址空间。运行时尝试向该对象发送消息,该对象认为占用该地址,并且由 Swift 运行时断言捕获,然后触发 EXC_BAD_INSTRUCTION 崩溃。

现在,对于在该地址占据住所的其他对象,UILabel 内容 的原始居民必须已被释放。那么为什么运行时会释放内容呢?因为它不再需要,即它不是它仍然需要的任何 View 的 subview 或属性。

我敢打赌,如果您将 content 更改为子类 UILabel 并添加一个 deinit 方法,然后断点,您将惊讶地发现它很早就被意外地取消初始化了。要测试这一点,请创建一个类型,如下所示:

class DebugLabel: UILabel
{
   func deinit
   {
     NSLog("Breakpoint on this line here!")
   }
}

然后将内容的类型更改为上面的DebugLabel

那么为什么会发生这一切呢?我的钱是你拥有一个以编程方式创建的 View 属性,该属性要么是弱的,要么是无主的。也许您之前使用 IBOutlet 设置了这些内容,然后将其删除,但忘记删除 weak 指示符?

仔细检查每一项,相信您会找到上述问题的原因。使用初始化程序或 UINib 以编程方式创建的任何内容都不应被指定为 weakunowned

关于swift - UILabel 如何成为 CALayer 并导致崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32859214/

相关文章:

swift - UIImageView 未正确 reshape

uiwebview - 应用传输安全性破坏了 Web View

ios - Swift 2 迁移问题

memory - 直接内存映射到 DIMM

ios - UI 测试期间无法触发照片删除窗口

swift - CollectionView 解析 JSON 后不显示数据

json - 将 JsonObject 传递给模型 SWIFT

ios - 快速更改播放器

c++ - Fortran 77 处理 C++ 内存分配

C++ 使用运算符重载构建双面树 - 什么是好的内存解决方案?