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

时间:2015-09-30 06:26:56

标签: 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
}()

2 个答案:

答案 0 :(得分:2)

这些问题总是非常难以追查。

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

有了这些错误,虽然崩溃线可以提供线索,但并不总是导致错误。其他地方已经发生了一些事情。

虽然Swift主要是内存管理,但对于那些粗心大意的人来说仍然存在陷阱。我个人看到了内存损坏的两个主要原因。第一个是由自引用闭包引起的保留周期。第二个 - 与您的问题更相关 - 与Storyboards和Xibs相关的视图。

如果我们通过逻辑方式遵循此规则,我们可以认为CALayer现在占用了UILabel 内容以前占用的地址空间。运行时尝试向对象发送消息是think占用该地址,并且由Swift运行时断言捕获,然后触发EXC_BAD_INSTRUCTION崩溃。

现在,对于其他一些在该地址居住的对象,必须释放UILabel 内容的原始居民。那么为什么运行时会发布内容?因为它不再是必需的,即它不是任何视图的子视图或属性,它仍然是必需的。

我敢打赌,如果你将内容更改为子类UILabel并添加一个deinit方法然后断点,你会惊讶地发现它是意外地在早期被初始化了。要测试这个,请按如下方式创建一个类型:

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

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

那为什么会发生这一切?我的钱是您的一个视图属性已经以编程方式创建为weakunowned。也许您之前使用IBOutlet设置了这些设置,然后删除但忘记删除weak指示符?

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

答案 1 :(得分:0)

简短的观看向我展示了两个潜在的问题:

  1. 你可以在这里破坏迭代器,导致未定义的行为 - removeFromSuperview()确实从层次结构中删除了视图并将其释放并减少了myView中的元素数量。
  2.   

    for myViews中的视图{       view.removeFromSuperview()}

    1. 你在这做什么?似乎你重复了上一步。
    2.   

      self.myViews.removeAll(keepCapacity:true)