UILabel怎么可能成为一名CALayer的人并导致崩溃呢?

时间:2022-09-10 19:14:51

I've got a custom UIView of which several instances are created inside a loop:

我有一个自定义的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)
}

I do sometimes see crashes in Crashlytics in the line with myView.content.text = model.content:

我有时会在myView.content中看到Crashlytics中的崩溃。文本= model.content:

UILabel怎么可能成为一名CALayer的人并导致崩溃呢?

According to the crash I assume it has something to do with memory, but I really don't know how the myView could have been released at that point.

根据这次崩溃,我认为它与内存有关,但我真的不知道myView当时是如何发布的。

All this happens in viewWillAppear:. Could the removing before has to do something with this? But I assume everything happens on the main thread, so this shouldn't be a problem as well - I'm really stuck here.

所有这些在viewWillAppear中发生:。之前的移除有什么用吗?但是我假设所有的事情都发生在主线上,所以这也不应该是一个问题——我真的被困在这里了。

The crash happens on iOS 9.

崩溃发生在iOS 9上。

EDIT

编辑

MyFaqView init method:

MyFaqView init方法:

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
}

EDIT

编辑

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

2 个解决方案

#1


2  

These problems are always very tricky to track down.

这些问题总是很难找到。

Basically, what is happening is memory corruption. The address 0x14f822a0 that was previously occupied by your UILabel content has been used by something else, in this case a CALayer. You can verify this if the crash happens locally by entering po 0x14f822a0 in lldb and sure enough it will output that address to be of type CALayer.

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

With these errors, although the crash line can provide a clue, it is not always the cause of the error. Something has already happened elsewhere.

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

Although Swift is mostly memory managed, there are still pitfalls for the unwary. Personally I have seen two principal causes of memory corruption. The first is with retain cycles caused by self referencing closures. The second - more pertinent to your problem - is with views related to Storyboards and Xibs.

尽管Swift大部分都是内存管理的,但对于粗心的人来说,仍然存在一些隐患。就我个人而言,我发现了记忆腐败的两个主要原因。第一种方法是使用自引用闭包引起的保留周期。第二个与您的问题更相关的是与故事板和xib相关的视图。

If we follow this through logically, we can consider that the CALayer now occupies the address space previously taken by your UILabel content. The runtime attempts to send a message to the object is thinks occupies that address, and that is caught by a Swift runtime assert which then triggers a EXC_BAD_INSTRUCTION crash.

如果我们从逻辑上理解这一点,我们可以考虑到,CALayer现在占据了之前由UILabel内容所占用的地址空间。运行时尝试向对象发送消息is think占用该地址,该地址被Swift运行时断言捕获,然后触发exc_bad_instructions崩溃。

Now, for some other object to have taken up residence at that address, the original inhabitant the UILabel content must have been released. So why would the runtime release content? Because it is no longer required, i.e. it is not a subview or property of any view that it still required.

现在,如果有其他物体在那个地址定居,那么UILabel的原始居住者一定已经被释放了。那么为什么运行时发布内容呢?因为不再需要它,也就是说,它不是仍然需要的任何视图的子视图或属性。

I would bet that if you change content to be a subclass UILabel and add a deinit method that you then breakpoint, you will be surprised to see that it is being unexpectedly deinitialised early on. To test this create a type as follows:

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

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

Then change content's type to be the DebugLabel above.

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

So why is all this happening? My money is on you having one of your view properties that has been created programmatically as being either weak or unowned. Perhaps you had these set up previously using an IBOutlet that you then removed but forgot to remove the weak designator?

为什么会这样呢?我的钱就花在你的视图属性上,你的视图属性是通过编程创建的,要么是弱属性,要么是空属性。也许您之前已经使用IBOutlet设置了这些内容,然后您删除了它,但忘记了删除弱指示器?

Check through each and every one carefully and I am sure you will find the cause of the problem above. Nothing that is created programatically either by using an initialiser or UINib should be designated weak or unowned.

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

#2


0  

Brief viewing shows me two potential problems:

简单的观察给我展示了两个潜在的问题:

  1. You can break iterator here, that cause undefined behavior - removeFromSuperview() really remove the view from the hierarchy and release it and reduce the number of elements in myViews.
  2. 您可以在这里中断迭代器,这会导致未定义的行为——removeFromSuperview()实际上从层次结构中删除视图并释放它,并减少myViews中的元素数量。

for view in myViews { view.removeFromSuperview() }

在myViews中查看。removefromsuperview ()}

  1. What do you do here? Seems you repeat the previous step.
  2. 你在这里做什么?看来你重复了前面的步骤。

self.myViews.removeAll(keepCapacity: true)

self.myViews。removeAll(keepCapacity:真)

#1


2  

These problems are always very tricky to track down.

这些问题总是很难找到。

Basically, what is happening is memory corruption. The address 0x14f822a0 that was previously occupied by your UILabel content has been used by something else, in this case a CALayer. You can verify this if the crash happens locally by entering po 0x14f822a0 in lldb and sure enough it will output that address to be of type CALayer.

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

With these errors, although the crash line can provide a clue, it is not always the cause of the error. Something has already happened elsewhere.

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

Although Swift is mostly memory managed, there are still pitfalls for the unwary. Personally I have seen two principal causes of memory corruption. The first is with retain cycles caused by self referencing closures. The second - more pertinent to your problem - is with views related to Storyboards and Xibs.

尽管Swift大部分都是内存管理的,但对于粗心的人来说,仍然存在一些隐患。就我个人而言,我发现了记忆腐败的两个主要原因。第一种方法是使用自引用闭包引起的保留周期。第二个与您的问题更相关的是与故事板和xib相关的视图。

If we follow this through logically, we can consider that the CALayer now occupies the address space previously taken by your UILabel content. The runtime attempts to send a message to the object is thinks occupies that address, and that is caught by a Swift runtime assert which then triggers a EXC_BAD_INSTRUCTION crash.

如果我们从逻辑上理解这一点,我们可以考虑到,CALayer现在占据了之前由UILabel内容所占用的地址空间。运行时尝试向对象发送消息is think占用该地址,该地址被Swift运行时断言捕获,然后触发exc_bad_instructions崩溃。

Now, for some other object to have taken up residence at that address, the original inhabitant the UILabel content must have been released. So why would the runtime release content? Because it is no longer required, i.e. it is not a subview or property of any view that it still required.

现在,如果有其他物体在那个地址定居,那么UILabel的原始居住者一定已经被释放了。那么为什么运行时发布内容呢?因为不再需要它,也就是说,它不是仍然需要的任何视图的子视图或属性。

I would bet that if you change content to be a subclass UILabel and add a deinit method that you then breakpoint, you will be surprised to see that it is being unexpectedly deinitialised early on. To test this create a type as follows:

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

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

Then change content's type to be the DebugLabel above.

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

So why is all this happening? My money is on you having one of your view properties that has been created programmatically as being either weak or unowned. Perhaps you had these set up previously using an IBOutlet that you then removed but forgot to remove the weak designator?

为什么会这样呢?我的钱就花在你的视图属性上,你的视图属性是通过编程创建的,要么是弱属性,要么是空属性。也许您之前已经使用IBOutlet设置了这些内容,然后您删除了它,但忘记了删除弱指示器?

Check through each and every one carefully and I am sure you will find the cause of the problem above. Nothing that is created programatically either by using an initialiser or UINib should be designated weak or unowned.

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

#2


0  

Brief viewing shows me two potential problems:

简单的观察给我展示了两个潜在的问题:

  1. You can break iterator here, that cause undefined behavior - removeFromSuperview() really remove the view from the hierarchy and release it and reduce the number of elements in myViews.
  2. 您可以在这里中断迭代器,这会导致未定义的行为——removeFromSuperview()实际上从层次结构中删除视图并释放它,并减少myViews中的元素数量。

for view in myViews { view.removeFromSuperview() }

在myViews中查看。removefromsuperview ()}

  1. What do you do here? Seems you repeat the previous step.
  2. 你在这里做什么?看来你重复了前面的步骤。

self.myViews.removeAll(keepCapacity: true)

self.myViews。removeAll(keepCapacity:真)