【发布时间】:2013-07-15 07:58:15
【问题描述】:
我们有选择地使用自动布局约束,主要用于定位与可编辑字段元素(通常是 UITextView、UITextField)相关的标签。然而,自从为这些字段实现自动布局以来,每当我们卸载视图、解除分配等时,我们都会看到一个令人讨厌的异常和崩溃。异常正在发生,因为它试图在卸载视图之前从视图中移除约束。
我们的视图/控制器层次结构如下:
UITableViewController (plain style, but with cell appearance to mimic grouped style)
--> UITableViewCell
----> UIViewController (container for editable form)
------> UICollectionViewController (editable form)
--------> UICollectionViewCell
-----------> UIViewController (editable field)
--------------> UILabel (field label) **HAS CONSTRAINTS**
--------------> UITextView / UITextField (field value) **HAS CONSTRAINTS**
很多时候,当上层表格单元被释放/替换/重新加载时,我们会看到一个巨大的异常,然后在它试图释放/卸载其中的视图层次结构时崩溃。
我试图通过捕获异常(无帮助)以及在解除分配/卸载(在viewWillDisappear: 中)之前强制删除受影响视图和所有子视图的所有约束来缓解崩溃似乎没有帮助。我什至试图一一消除这些限制,看看是否有一个特别导致问题的因素,但是当我们在容器上调用 removeConstraint: 或 removeConstraints: 以准备消失时,所有这些限制都会爆炸。
我很困惑!这是我们的异常的 sn-p —— 大约有 3000 行被删掉了,所以如果你需要更多,尽管问。
Exception while deallocating view: { Rows:
0x18911270.posErrorMarker == 4 + 1*0x18911270.negError + 1*0x189112f0.marker + -1*0x189113f0.negError + 1*0x189113f0.posErrorMarker + 1*0x18911a60.marker + -0.5*0x1892dae0.negError + 0.5*0x1892dae0.posErrorMarker + 1*0x18951520.negError + -1*0x18951520.posErrorMarker + -0.5*0x18958090.negError + 0.5*0x18958090.posErrorMarker
0x189112b0.negError == 12 + 1*0x189112b0.posErrorMarker + -1*0x189112f0.marker + 1*0x189113f0.negError + -1*0x189113f0.posErrorMarker + -1*0x18911a60.marker + 1*0x18925530.marker + 0.5*0x1892dae0.negError + -0.5*0x1892dae0.posErrorMarker + 1*0x1893e080.marker + 0.5*0x18958090.negError + -0.5*0x18958090.posErrorMarker + 1*0x18963640.marker
0x18911370.negError == 9 + -1*0x189112f0.marker + 1*0x18911370.posErrorMarker + 1*0x18925530.marker + 1*0x1892dae0.negError + -1*0x1892dae0.posErrorMarker + 1*0x1893e080.marker + 1*0x18963640.marker
0x189113b0.slackMarker == 2 + -1*0x189107d0.marker + 1*0x18910b90.negError + -1*0x18910b90.posErrorMarker +
........ EXPLETIVES DELETED .........
UITableView:0xca2b000.contentHeight == 36 + 1*0xc221c00.marker
UITableView:0xca2b000.contentWidth == 704 + 1*0xc239470.marker
UITableView:0xca2b000.minX == 0 + 1*0xc2a23f0.marker + -0.5*0xc2a2590.marker
UITableView:0xca2b000.minY == 0 + 1*0xc2a25d0.marker + -0.5*0xc2a2630.marker
UITableViewCellContentView:0x18ab13d0.Height == 174 + 1*0x18abd4f0.marker
UITableViewCellContentView:0x18ab13d0.Width == 704 + 1*0x18abd470.marker
........ EXPLETIVES DELETED .........
<NSAutoresizingMaskLayoutConstraint:0x18988bc0 h=-&- v=-&- UIView:0x18911e50.midY == UIView:0x1892d0c0.midY> Marker:0x18988bc0.marker
<NSAutoresizingMaskLayoutConstraint:0x18994b40 h=-&- v=-&- UIView:0xc4a6fb0.midX == UIView:0xc4b4990.midX> Marker:0x18994b40.marker
<NSAutoresizingMaskLayoutConstraint:0x18998480 h=-&- v=-&- UIView:0x18915180.width == UIView:0xc4c5970.width> Marker:0x18998480.marker
<NSAutoresizingMaskLayoutConstraint:0x18aae320 h=--& v=--& TapSectionalTableViewCell:0x18a3d270.midX == + 352> Marker:0x18aae320.marker
<NSAutoresizingMaskLayoutConstraint:0x18aae410 h=--& v=--& H:[TapSectionalTableViewCell:0x18a3d270(704)]> Marker:0x18aae410.marker
<NSAutoresizingMaskLayoutConstraint:0x18aae450 h=--& v=--& TapSectionalTableViewCell:0x18a3d270.midY == + 144> Marker:0x18aae450.marker
........ EXPLETIVES DELETED .........
<NSAutoresizingMaskLayoutConstraint:0xc2de2f0 h=--& v=--& TapGenericCollectionCell:0xc2ac500.midX == + 499> Marker:0xc2de2f0.marker
<NSAutoresizingMaskLayoutConstraint:0xc2de3b0 h=--& v=--& V:[TapGenericCollectionCell:0xc2ac500(34)]> Marker:0xc2de3b0.marker
<NSAutoresizingMaskLayoutConstraint:0xc2de430 h=-&- v=-&- UIView:0x18953f80.height == UIView:0xc2acb20.height> Marker:0xc2de430.marker
<NSAutoresizingMaskLayoutConstraint:0xc2de520 h=-&- v=-&- UIView:0x18923af0.height == UIView:0xc2ae570.height> Marker:0xc2de520.marker
<NSAutoresizingMaskLayoutConstraint:0xc2de560 h=--& v=--& H:[TapGenericCollectionCell:0xc2ac500(280)]> Marker:0xc2de560.marker
........ EXPLETIVES DELETED .........
<NSContentSizeLayoutConstraint:0xc2f5730 H:[_UIBaselineLayoutStrut:0x18994a30(0)] Hug:250 CompressionResistance:750> Marker:0xc2f5730.posErrorMarker
<NSContentSizeLayoutConstraint:0xc2f5730 H:[_UIBaselineLayoutStrut:0x18994a30(0)] Hug:250 CompressionResistance:750> Marker:0xc2f5730.posErrorMarker
<NSContentSizeLayoutConstraint:0xc2f5770 V:[_UIBaselineLayoutStrut:0x18994a30(18)] Hug:250 CompressionResistance:750> Marker:0xc2f5770.posErrorMarker
internal error. Cannot find an outgoing row head for incoming head UIView:0x189712b0.Width, which should never happen.'
/**** BEGIN Individual Field Controller - This code is from the base individual field controller used in our editable form collection *****/
- (void)viewDidLoad {
[super viewDidLoad];
self.view.clipsToBounds = YES;
self.view.opaque = YES;
CGRect viewFrame = self.view.frame;
viewFrame.size = [self defaultFieldSize];
self.view.frame = viewFrame;
if (self.backgroundColor) {
self.view.backgroundColor = self.backgroundColor;
}
else {
self.view.backgroundColor = [UIColor whiteColor];
}
[self createLabelAndField];
[self setLabelAndFieldContraints];
[self.view addConstraints:self.labelValueConstraints];
[self.view setNeedsUpdateConstraints];
}
- (void)createLabelAndField {
[self removeLabelAndField];
UILabel *label = [[UILabel alloc] init];
label.font = self.labelFont;
label.textColor = self.labelColor;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.textAlignment = NSTextAlignmentLeft;
label.adjustsFontSizeToFitWidth = NO;
label.numberOfLines = 0;
if (self.backgroundColor) {
label.backgroundColor = self.backgroundColor;
}
else {
label.backgroundColor = [UIColor whiteColor];
}
[self.view addSubview:label];
self.label = label;
/// EXAMPLE valueView initialization from a subclass that handles long text
TapEditableTextView *textView = [[TapEditableTextView alloc] init];
if (self.hasLabelOverValue) {
textView.shouldMimicTextField = NO;
}
else {
textView.shouldMimicTextField = YES;
}
textView.delegate = self;
textView.keyboardType = UIKeyboardTypeDefault;
textView.font = self.valueFont;
textView.textColor = self.valueColor;
textView.textAlignment = NSTextAlignmentLeft;
textView.normalBackgroundColor = self.backgroundColor;
textView.editable = NO;
textView.textLines = self.textLines;
self.valueTextView = textView;
self.valueView = textView;
[self.view addSubview:textView];
}
- (void)removeLabelAndField {
[self clearConstraints];
if (self.label) {
[self.label removeFromSuperview];
self.label = nil;
}
if (self.valueView) {
[self.valueView removeFromSuperview];
self.valueView = nil;
}
}
- (void)clearConstraints {
if (self.isViewLoaded && self.labelValueConstraints) {
[self.view removeConstraints:self.labelValueConstraints];
}
self.labelValueConstraints = nil;
self.labelToValueHorizConstraint = nil;
self.valueWidthConstraint = nil;
}
// This is called in our field's viewDidLoad, after we've created our label and valueView (UITextField, UITextView, etc)
- (void)setLabelAndFieldContraints {
[self clearConstraints];
self.labelValueConstraints = [NSMutableArray array];
self.label.translatesAutoresizingMaskIntoConstraints = NO;
self.valueView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint = nil;
constraint = [NSLayoutConstraint
constraintWithItem:self.label attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeLeft
multiplier:1.0f constant:self.labelValueGap];
constraint.priority = UILayoutPriorityRequired;
[self.labelValueConstraints addObject:constraint];
constraint = [NSLayoutConstraint
constraintWithItem:self.label attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeTop
multiplier:1.0f constant:0];
constraint.priority = 550;
[self.labelValueConstraints addObject:constraint];
constraint = [NSLayoutConstraint
constraintWithItem:self.label attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeBottom
multiplier:1.0f constant:0];
constraint.priority = 400;
[self.labelValueConstraints addObject:constraint];
constraint = [NSLayoutConstraint
constraintWithItem:self.valueView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeTop
multiplier:1.0f constant:0];
constraint.priority = UILayoutPriorityRequired;
[self.labelValueConstraints addObject:constraint];
constraint = [NSLayoutConstraint
constraintWithItem:self.valueView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeBottom
multiplier:1.0f constant:0];
constraint.priority = 499;
[self.labelValueConstraints addObject:constraint];
constraint = [NSLayoutConstraint
constraintWithItem:self.valueView attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeRight
multiplier:1.0f constant: -(kDisclosureWidth + self.labelValueGap) ];
constraint.priority = 901;
[self.labelValueConstraints addObject:constraint];
constraint = [NSLayoutConstraint
constraintWithItem:self.valueView attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:self.label attribute:NSLayoutAttributeTrailing
multiplier:1.0f constant:self.labelValueGap];
constraint.priority = UILayoutPriorityDefaultHigh + 1;
[self.labelValueConstraints addObject:constraint];
self.labelToValueHorizConstraint = constraint;
constraint = [NSLayoutConstraint
constraintWithItem:self.label attribute:NSLayoutAttributeBaseline
relatedBy:NSLayoutRelationEqual
toItem:self.valueView attribute:NSLayoutAttributeBaseline
multiplier:1.0f constant:0.f];
constraint.priority = 600;
[self.labelValueConstraints addObject:constraint];
constraint = [NSLayoutConstraint
constraintWithItem:self.valueView attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeWidth
multiplier:(1.f - self.labelWidthPercentage) constant:0];
constraint.priority = 305;
[self.labelValueConstraints addObject:constraint];
self.valueWidthConstraint = constraint;
[self setCompressionAndHuggingForLabelView:self.label];
[self setCompressionAndHuggingForValueView:self.valueView];
}
- (void)setCompressionAndHuggingForLabelView:(UILabel *)labelView {
if (!labelView) {
return;
}
[labelView setContentCompressionResistancePriority:510 forAxis:UILayoutConstraintAxisHorizontal];
[labelView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
[labelView setContentHuggingPriority:450 forAxis:UILayoutConstraintAxisHorizontal];
[labelView setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
}
- (void)setCompressionAndHuggingForValueView:(UIView *)valueView {
if (!valueView) {
return;
}
[valueView setContentCompressionResistancePriority:509 forAxis:UILayoutConstraintAxisHorizontal];
[valueView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
[valueView setContentHuggingPriority:300 forAxis:UILayoutConstraintAxisHorizontal];
[valueView setContentHuggingPriority:650 forAxis:UILayoutConstraintAxisVertical];
}
/****** END Individual Field Controller ******/
【问题讨论】:
-
只是好奇,您是在后台线程上进行这些更改吗?其次,viewDidDisappear 而不是 viewWillDisappear 呢?
-
另外,1) Cocoa Touch 框架中的异常被视为致命错误;不要试图抓住它们来解决它们,2)我们可以看到一些代码吗?
-
请注意,我认为当您添加所有这些约束时,您不需要调用
setNeedsUpdateConstraints,而是调用setNeedsLayout。根据updateConstraints上的文档,“自己设置约束的自定义视图应该通过覆盖此方法来实现。”怀疑这是问题所在。代码看起来一般。一般需要有关您的应用程序的更多信息:您在使用 CoreData 吗?什么是“可编辑表单集合”?这里的结构是什么? :) -
我明白了。好吧,我的直觉说在后台线程上发生了某种调用,或者以某种方式违反了 UIKit 的程序。这就是你得到 NSInternalInconsistency 错误的原因;我过去也遇到过类似的事情。但是如果没有看到你的整个项目,我真的无法发现它。类似的建议在这里:twitter.com/radiantav/status/296588220626509824 ...如果您认为某人的推文是权威 XD
-
脏话?给我看未经审查的例外!
标签: ios automatic-ref-counting autolayout