主に編集可能なフィールド要素(通常は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:
)そして、それは助けにならないようです。問題を引き起こしている特定の制約があるかどうかを確認するために、これらの制約を1つずつ削除しようとしましたが、removeConstraint:
またはremoveConstraints:
消える準備をしているコンテナ。
私は困惑しています!例外の抜粋を次に示します。約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 ******/
私は、このクラッシュについてAppleエンジニアと会話)をしました。
最も可能性の高い2つの原因を次に示します。
view1.left = view2.left + 20
のような無効な制約があります。ここで、view2
は予期しないnilであるか、乗数が0です。制約が正しいことを確認するために、制約を二重(および三重)にチェックしてください。問題のある制約の2つの例を次に示します。
// The first constraint would be a problem if view2 were nil
[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeBottom multiplier:1 constant:20];
// The second constraint is a problem because the 0 multiplier causes view2 to be "lost"
[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeBottom multiplier:0 constant:5];
浮動小数点精度の累積損失に関連する内部Foundation自動レイアウトエンジンのバグに遭遇しています。クラッシュした場合、これが事実であることを知る方法は、コンソールの(大きな)例外ログを検索して、次のような非常に小さな(ほぼゼロの)浮動小数点数を探すことです。
<505:-7.45058e-08>*PWPlotLegendEntryView:0x600000582be0.Height{id: 34609} +
(コンソール出力でe-
を検索して、このような小さな数字を見つけます。)この数字(この場合は-7.45058e-08
)は、内部エンジンが制約を解決している間のこの特定の時点の係数を表します。この場合、数値は正確に0であると想定されていますが、自動レイアウトエンジンが浮動小数点数を使用して計算する方法により、非常に小さな負の数になり、すべてが爆発します。出力でそのような番号を見つけることができれば、このバグにぶつかったことがわかります。
この問題をどのように回避できますか?
制約を追加(アクティブ化)する順序を変更すると、内部エンジンの計算の順序が変更される可能性があります。その結果、精度を問題なく損なうことなく数学が行われるため、この問題が消える可能性があります。
この問題は、ビューのコンテンツ圧縮耐性またはコンテンツハグの優先順位を変更した場合に頻繁に発生するようです。そのため、このバグが発生するかどうかを確認するコードをコメントアウトするか、より早く発生するように並べ替えてください。またはそれ以降の制約設定コード。
特定のケースの詳細:
IOSでこのクラッシュに遭遇しました。それを再現する手順は非常に興味深いものでした。
多くの試行錯誤の後、問題を特定の1つに切り分けることができました。各テーブルビューセルのUIImageView
のコンテンツ圧縮耐性とハグの優先順位を設定しました。この場合、画像ビューはセル内の自動レイアウトを使用して配置されます。正しいレイアウトを実現するには、画像ビューが本来のコンテンツサイズ(画像のサイズ)である必要があります。
これは問題のあるコードでした:
// Inside of the UITableViewCell's updateConstraints method...
[self.imageView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.imageView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.imageView setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.imageView setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
上記のコードを削除し、2つの制約(必要な優先度で)に置き換えて画像ビューの幅と高さを画像のサイズに固定すると、同じ結果が得られましたが、クラッシュは回避されました。置換コードは次のとおりです( PureLayout を使用):
[self.imageView autoSetDimensionsToSize:self.imageView.image.size];
また、問題のある4行を制約設定コードの別の場所に移動するだけで問題が解決したこともわかりました。
自動レイアウトで動作するコードはメインスレッドで実行される場合がありますが、バックグラウンドで実行され、ビューを使用する(おそらく間接的に)ブロックの1つは、ビューまたはビューコントローラーのような所有者の強力な参照を保持します( Objective-Cブロックのデフォルトの動作)。そのようなブロックが実行され、バックグラウンドキューで割り当て解除されると、それがキャプチャする強参照が同じキューで解放され、よく知られている 割り当て解除の問題 に直面する可能性があります。
View Controllerで、強い参照を必要としないneedすべてのブロックでself
への弱い参照を使用していることを確認してくださいバックグラウンドで実行)。次のように宣言できます:__weak typeof(self) weakSelf = self;
ブロックの前—そしてブロック内でweakSelf
を使用します。
ビューへの参照を保持するローカル変数についても同様です。それらの値が弱い参照としてキャプチャされることを確認してください。
私の仕事では、非表示のビューがレイアウトに参加しているときに、iOS 6で同様の問題が発生しました。階層からビューを削除(-[UIView removeFromSuperview]
)hidden
プロパティをYESに設定する代わりに、問題を修正しました。
同じ問題があった場合、クラッシュが解決されるまでIBで制約を1つずつ削除することで解決しました。これにより、問題のある制約に絞り込まれました。その後、上記の制約を回復しましたが、項目を逆にしました。
幸運にも、ALの問題を簡単に解決できるかもしれません。
@smileyborgの素晴らしい答えをより実用的にするには:
これは、浮動小数点の精度の問題が発生する可能性のある乗数の制約がある場合に発生する可能性があります。
解決するには:
multiplier=
)。2を行う簡単な方法は、希望する値と計算機を入力し、計算機の下部の丸められた10進値と一致するまで仮数の低精度ビットをオフにすることです。
IOSバージョン> 8.0でこの問題が発生した場合、Apple docsは、UIViewのremoveConstraint/addConstraint関数ではなく、NSLayoutConstraintの「アクティブ」プロパティを使用するように指定します。 AppleドキュメントaddConstraintリファレンス
私の場合、8:9乗数の比例幅制約でした。私はそれを7:9に変更し、うまくいきました。
ところで、制約を見つける最も簡単な方法は、View Controllerからビューの削除を開始することです。バイナリアルゴリズムを使用して実行します。
私にとって問題は、UICollectionViewCellのプロパティを設定しながらdequeueReusableCellWithReuseIdentifierを呼び出した後、自分に合った制約を一度に削除していたことです。解決策は、代わりに呼び出すことでした:
[_myUICollectionViewCell setNeedsUpdateConstraints];
そしてオーバーライド:
-(void)updateConstraints
そこをめちゃくちゃにします。好きなときに制約を削除することはできないようです。
WAnyhAnyモードでまだ制約が欠落しているときにこのクラッシュが発生しました。これを修正するとエラーが削除されました。
開発中のOSXアプリでOSX Mavericksの下で同じエラーに出くわしましたが、他の回答とは異なり、UIオブジェクトと相互作用する他のスレッドは絶対になく、問題のビュー階層は間違いなく表示されていますも。私もブロックを使用していません。奇妙なことに、NSTextFieldの垂直制約を削除すると、問題はなくなりました。
FWIWそのスーパービューからの削除が「内部エラー。着信ヘッドの発信行ヘッドを見つけられない」エラーを引き起こす問題のあるビューは、カットできるメインビュー内のオブジェクトのプロパティを一緒に表示する多くのサイドパネルコントロールの1つです。これは、ユーザーが新しいオブジェクトをメインビューに非常に迅速に貼り付けることができることを意味します。つまり、サイドパネルのコントロールが破壊され、新しいコントロールも非常に迅速に作成されます。もちろん、メインスレッドのすべてで、これは違いを生むべきではありませんが、そうです。
問題を引き起こす正確な制約は
[self addConstraint:[NSLayoutConstraint constraintWithItem:control attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:other attribute:NSLayoutAttributeHeight multiplier:1.4 constant:0.0]];
ここで、controlは問題を引き起こす(編集可能な)NSTextFieldであり、「other」は別の(編集不可能な)NSTextFieldラベルです。
MZFormSheetController
podでこの問題が発生しました: https://github.com/m1entus/MZFormSheetController/issues/78
このコードはクラッシュします:
[formSheetController.view addSubview:self.sharePanel];
// ...
[self.sharePanel removeFromSuperview]; // <-- CRASHES HERE
私の解決策は非常に奇妙ですが、うまくいきます:
[self.sharePanel removeFromSuperview]; // <-- This line helps to avoid crash
[formSheetController.view addSubview:self.sharePanel];
// ...
[self.sharePanel removeFromSuperview];
そして、これはsharePanel
プロパティ宣言です:
@property (weak, nonatomic) IBOutlet UIView *sharePanel;
Apple Documentation:
IOS 8.0以降向けに開発する場合、addConstraint:メソッドを直接呼び出す代わりに、制約のactiveプロパティを[〜#〜] yes [〜#〜]に設定します。アクティブなプロパティは、正しいビューから自動的に制約を追加および削除します。
私の場合、幅の制約を変更する必要がありました
for var constraint in self.navigationBar.constraints {
if constraint.identifier == "theProgressWidth" {
let sizeWidth = self.navigationBar.frame.size.width
constraint = NSLayoutConstraint(item: progress!, attribute: .Width, relatedBy: .Equal, toItem: self.navigationBar, attribute: .Width, multiplier: ((sizeWidth * (level / 100)) / sizeWidth), constant: 0)
constraint.active = true
}
}
RemoveConstraints:をnil引数で呼び出すと、このクラッシュが発生します。
このスレッドの他の答えは、これが何らかの理由で無効な自動レイアウト/制約の問題であることを示していますが、「無効」と見なされるものについては非常に細心の注意を払っているようです。
幸運なことに、最後のコミット以降、多くの変更を加えておらず、問題のある変更を追跡することができました。私にとって、10の水平UIImageView
を同じ幅で固定し、2:3のアスペクト比を固定することが問題でした。
クラッシュは、この画像行を含むUIViewController
を離れた後にのみ発生するように見えました。各UIImageView
はUIViewContentModeScaleAspectFill
に設定されました。このコンテンツモードの変更(UIImage
sが設定される前に行われた)を削除すると、私の問題は解決したようですが、許容できる解決策ではありませんでした。アスペクト比の制限を取り除き、各画像の幅と高さを固定して使用しました。
なぜこれが私のアプリケーションをクラッシュさせたのかは知りません...クラッシュは、iOS 7.1.2を実行しているiPhone 4で[〜#〜] only [〜#〜]を再現することもできます。 iOS 9.1を実行しているiPhone 4sシミュレーターで同じクラッシュを再現しようとしましたが、成功しませんでした。また、iOS 9.1を実行する物理的なiPhone 5で実行してもクラッシュしません。
それが誰かを助けることを願っています