AutoLayout: removeFromSuperview / removeConstraints throws exception and crashes hard
Asked Answered
H

14

56

We use auto layout constraints selectively, primarily to position labels in relation to editable field elements (UITextView, UITextField, typically). However, since implementing auto layout for these fields, we're seeing a nasty exception and crash whenever we're unloading views, deallocating, etc. The exceptions are happening as it's attempting to remove the constraints from a view before unloading it.

Our view/controller hierarchy is as such:

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**

Many times when the upper level table cells are being deallocated/replaced/reloaded, we see a huge exception and then crash as it's trying to deallocate/unload the view hierarchy within.

I've attempted to mitigate the crash by catching the exception (no help) and also by forcefully removing all of the constraints on the affected view and all of the subviews prior to deallocation/unload (in viewWillDisappear:) and it doesn't seem to help. I've even tried to remove these constraints one by one to see if there's one in particular that's causing the trouble but all of them are blowing up when we call removeConstraint: or removeConstraints: on a container in preparation for disappearing.

I'm baffled! Here's a snippet of our exception -- roughly about 3000 lines have been chopped out of it, so if you need more, just ask.

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 ******/
Humberto answered 16/7, 2013 at 22:59 Comment(7)
Just curious, are you doing these changes on a background thread? Secondly, what about viewDidDisappear instead of viewWillDisappear?Merger
Additionally, 1) Exceptions in the Cocoa Touch framework are meant as fatal errors; do not attempt to catch them to solve them, 2) Could we see some code?Merger
I'll double-check but I'm almost certain none of this is backgrounded. Yep, catching the thread was a last-ditch effort (failed one at that). Adding some code from our form field controller above...Humberto
One note, I think when you add all those constraints, you don't need to call setNeedsUpdateConstraints but rather setNeedsLayout. Per the docs on updateConstraints "Custom views that set up constraints themselves should do so by overriding this method." Doubt that's the problem, though; code looks fine in general. Need more information about your app in general: are you using CoreData? What is an "editable form collection"? What's the structure here? :)Merger
I am using Core Data, but not for this chain, this is using dictionaries for in-memory changes. The general idea is that we have a dynamic UI for data entry. Dynamic, because depending on some JSON metadata, we want to render the view with date-pickers here, long text there, an image picker down there, some boolean switches, and a pseudo-combo-box at the end... don't know what components I'll be showing (where) until I see the metadata at runtime. A modal TableVC comes up, has groups, each group has a collection of 1 or more editable fields based on the common field code above.Humberto
I get it. Well, my instinct says that there is some kind of call occurring on a background thread or in some way that violates UIKit's procedures. That's why you get NSInternalInconsistency errors; I've run into similar things in the past. But I really couldn't spot it without seeing your whole project. Similar advice here: twitter.com/radiantav/status/296588220626509824 ...If you consider someone's tweet an authority XDMerger
Expletives? Show me the uncensored exception!Wahlstrom
C
95

I had an (extensive) conversation with an Apple engineer about this crash.

Here are the two most probable causes:

  1. You have an invalid constraint, such as view1.left = view2.left + 20 where view2 is unexpectedly nil, or has a multiplier of 0. Be sure to double (and triple) check your constraints to make sure that they are correct. Here are 2 examples of problematic constraints:

    // 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];
    
  2. You're hitting a bug in the internal Foundation auto layout engine related to accumulated loss of floating point precision. When you've crashed, the way you can know that this is the case is to search through the (large) exception log in the console for a very small (nearly zero) floating point number such as this:

<505:-7.45058e-08>*PWPlotLegendEntryView:0x600000582be0.Height{id: 34609} +

(Search for e- in the console output to find small numbers like this.) This number (-7.45058e-08 in this case) represents the coefficient at this particular point in time while the internal engine is solving constraints. In this case, the number is supposed to be exactly 0, but due to the way the auto layout engine does calculations with floating point numbers it has become an extremely tiny negative number instead which blows everything up. If you can find such a number in the output, you know that you've hit this bug.

How can you work around this issue?

Changing the order that you add (activate) constraints can end up changing the order of calculations in the internal engine, which as a result can cause this issue to disappear as the math is done without any problematic loss of precision.

This issue seems to come up more frequently when you have changed the content compression resistance or content hugging priorities for views, so try commenting out any code that does that to see if it's causing this bug to happen, or re-ordering it to happen earlier or later in your constraint setup code.

More details about my specific case:

I ran into this crash on iOS. The steps to reproduce it were quite interesting:

  1. A view controller containing a table view was pushed on screen (in a navigation controller).
  2. The table view had to contain enough cells so they didn't all fit in the visible area, then it had to be scrolled to the last cell and then back up a bit (presumably, this was causing cells to be reused, which was triggering this issue).
  3. Then, when the view controller containing the table view was popped off the navigation stack, immediately after the pop animation completed the app would crash at the point where the view controller's view was removed from the view hierarchy.

After a lot of trial and error I was able to isolate the issue to one specific thing: setting the content compression resistance & hugging priorities for a UIImageView in each of the table view cells. In this case, the image view is being positioned using Auto Layout inside the cell, and to achieve the correct layout the image view needs to be exactly its intrinsic content size (the size of its image).

This was the problematic code:

// 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];

Removing the above code and replacing it with 2 constraints (at Required priority) to fix the width & height of the image view to the image's size achieved the same result, but avoided the crash. Here's the replacement code (using PureLayout):

[self.imageView autoSetDimensionsToSize:self.imageView.image.size];

I also found that just moving the problematic 4 lines to a different place in my constraint setup code resolved the issue, presumably because this changed the order of calculations sufficiently to prevent the problematic loss of precision.

Cephalometer answered 4/12, 2014 at 0:9 Comment(16)
This answer saved our bacon! Our view was crashing only on the iPad 2, due to the floating point error. Setting a multiplier from 1.6 to 1.59999 fixed it.Bendick
@Cephalometer -- This is some fantastic investigative work... Great answer!Humberto
I had a constraint with a multiplier on. The multiplier was 0.3333. When the view was removed from its superview, the app crashed with the signature mentioned. I changed the relation from NSLayoutRelationEqual to NSLayoutRelationGreaterThanOrEqual which fixed the problem.Pastoralist
I encountered the very same issue, where the modal view I had pushed with sub views laid out using Autolayout had crashed when I popped it again. I added a constraint to a UIView contained within setting the width 0.8 as a multiplier for the view controllers view, where the constraint was added. This was done in code. Setting the value to anything else other than 0.8 means the app does not crash. A very odd bug.Driftage
Awesome answer! Autolayout is another leaky abstraction, I love it!Vineland
This was definitely the issue for me. I had a proportional width view based on the superview width, and the crash indicated the rounding problem was at fault. I reworked my constraints such that I no longer needed the proportional width at all, and the crash disappeared for me. Thanks for the pointers!Osmond
I've run in to this a few more times since I posed the original question. For my situations it seems to be a combination of: (1) the degrading floating point precision you mention above (2) removing one constraint at a time instead of all the locally-created constraints at once (use deactivateConstraints now) and (3) having unnecessarily "required" priorities on constraints that aren't unary -- I now use a pool of uniquely assigned priorities on those constraints ranging from 880-995 -- never 1000 unless it's unary.Humberto
Great answer, my case was two proportional widths to superview with some offset, 0.6 * superview.width - 10 and 0.4 * superview.width - 10. Changing the second one to 0.401 solved the issue. Thanks !Skimpy
Seeing meaningless error like is awful. Your answer saved me. What I did was removing constraint of my views and redo them. Only happen on iOS7 for me btw. iOS8 doesn't seem to have this problem or at least no with the config I am using.Thagard
One of the best answers at StackOv! I changed all my multipliers to numbers like 1.999, 0.333, 1.449, 0.799, etc. (Thanks to comments that suggest that).Commissure
By your comments, I see that some multipliers like 0.4, 0.8, 1.6, etc. raise this bug. I have been playing with this float converter but haven't found the reason why these numbers crash. It may have something to do with their bit pattern but don't know what. Maybe someone can shed some light on this.Commissure
@FerranMaylinch please read my answer above. The issue isn't directly caused by the multipliers used; it's caused internally in the auto layout engine that solves all of your constraints.Cephalometer
@Cephalometer Oh, yes, thanks! I read your answer a couple of times, haha. I understood it's caused by the engine, but I guess the engine struggles with some floating point numbers, doesn't it? Did the engineer tell you about the exact cause?Commissure
@FerranMaylinch the coefficients that you see in the auto layout trace are part of the internal state of each constraint (linear equation) as the entire system is solved. These coefficients are the result of many rounds of mathematical operations. As you know, floating point numbers are not exact representations. So for certain inputs & operations, some of the intermediate state (coefficients) can end up losing precision which causes this crash. There's no way to predict or avoid which coefficients will end up coming up, and therefore no way to really avoid this if it is going to happen.Cephalometer
Same situtation for me. Using a multiplier of 1.5999 instead of 1.6 fixed the issue.Tillery
Happened to me as well without any code, only storyboards with lots of constraints with multipliers. Manually editing the .storyboard file replacing each multiplier with something that doesn't suffer from floating point precision issues (used h-schmidt.net/FloatConverter/IEEE754.html to generate them) fixed the crash for me (happened on iOS 8.1.3, iPad 2)Epifocal
I
14

Deallocation problem — one possibility

Your code that works with auto-layout may well run on the main thread but one of the blocks that's run in background and uses your view (perhaps indirectly), may hold a strong reference the view or one of its owner like view controller (that's the default behavior of Objective-C blocks). When such a block is run and deallocated on a background queue, strong references that it captures are released on that same queue and you may face a well known deallocation problem.

  1. In your view controller, make sure you are using weak reference to self in all blocks that do not need a strong reference (and may run in background). You can declare it like this: __weak typeof(self) weakSelf = self; before the block — and use weakSelf inside the block.

  2. Same goes for any local variables that hold references to your views — make sure their values are captured as weak refs.

Another possibility

In my work, I've encountered a similar issue on iOS 6 when hidden view participated in layout. Removing the view from hierarchy (-[UIView removeFromSuperview]) instead of setting hidden property to YES fixed the issue for me.

Itu answered 2/12, 2013 at 20:5 Comment(1)
Excellent points. I don't know if this is what resolved my issues (I've since refactored a good portion of the related code) but this is certainly plausible. I didn't consider that deallocation problem -- that's brilliant.Humberto
F
3

Had the same issue, solved it by deleting constraints one at a time in IB until the crash was solved. This narrowed it down to the offending constraint. I then reinstated said constraint, but reversed the items:

enter image description here

You may be as lucky and be able to solve your AL problems as easily.

Freighter answered 31/3, 2015 at 3:4 Comment(3)
Of course, why would a symmetric operator (equal) be symmetric?Vineland
@RhythmicFistman Because of the way constraints get added into the auto layout engine (NSISEngine), constraints that appear to be symmetric may not be in reality. At a minimum, reversing the sides may change the order that the calculations are performed, which may expose (or avoid) Apple's bugs in SDK.Cephalometer
Realising that autolayout's constraint solver uses floating point goes a long way to explaining some of its bizarre behaviour! I never thought about that. Thanks, @smileyborg!Vineland
E
3

To make @smileyborg's awesome answer more actionable:

This can happen if you have any constraints with multipliers that might suffer from floating point precision issues.

To solve:

  1. Go over all your constraints that have multipliers (in layout code or by manually editing a storyboard/nib and searching for multiplier=).
  2. If multiplier isn't a "pretty" power of two float, turn it to the nearest one (you can use a floating point calculator)

To easy way to do 2 is to enter the number you value you want and the calculator and then switch off lower precision bits in the mantissa until the value matches the rounded decimal value at bottom of the calculator.

Epifocal answered 8/3, 2016 at 22:14 Comment(0)
G
2

For anyone encountering this issue in any iOS version > 8.0, the Apple docs state to use the "active" property on the NSLayoutConstraint rather than the removeConstraint/addConstraint functions on UIView. Apple Docs addConstraint reference

Grant answered 15/4, 2016 at 22:4 Comment(0)
T
1

I am getting this crash when I call removeConstraints: with a nil argument.

Turd answered 2/12, 2014 at 15:9 Comment(0)
A
1

In my case it was a proportional width constraint with 8:9 multiplier. I changed it to 7:9 and it worked.

BTW the easiest way to find a constraint is to start removing views from view controller. Do it using binary algorithm :) removing half views, then half of the half that makes app crash, etc.

Armilla answered 5/6, 2015 at 13:28 Comment(2)
In your case, this sounds like the floating point precision problem that @Epifocal refers to.Humberto
Correct, but when I posted the answer, yonix's answer didn't exist.Armilla
V
1

For me the problem was that I was removing a constraint at a time that suited me, after calling dequeueReusableCellWithReuseIdentifier while setting properties of my UICollectionViewCell. The solution was to instead call:

    [_myUICollectionViewCell setNeedsUpdateConstraints];

and override:

    -(void)updateConstraints 

and do my messing there. Seems that you can't just remove constraints when you like.

Venice answered 16/7, 2015 at 19:41 Comment(0)
C
1

I just spent the weekend trying to figure out the reason our app crashed with the debug console being filled with rows saying:

Failed to rebuild layout engine without detectable loss of precision. This should never happen. Performance and correctness may suffer.

This post led me in the right direciton, as the error turned out to be caused by having several constraints with multipliers set to arbitrary floating points with bad precision, as @yonix explained in his answer.

In our case the multipliers was dynamicaly set by the user draging a shape across the screen, ranging from 0 to 1.0, so naturally there would be cases where the multipliers required lots of bits set in the mantissa to be well defined.

By aligning the multipliers to a multiple of 1/2^n, the multipliers can be precisely defined with less bits in the mantissa, and the issue is averted.

We figured 9 bits (1/2^9) would be sufficient for us, and aligning the multiplier can be done by just multiplying by the factor, truncating and the dividing by the factor again.

    let factor = 512.0 // 9-bits alignment
    let alignedMultiplier = (multiplier * factor).rounded() / factor

Ensuring that all user defined multipliers are aligned before using them in our constraints, solved our problem.

So I add to this post for future reference, in case anyone else stumbles across the same issue.

Culex answered 24/10, 2022 at 22:17 Comment(0)
J
0

I just stumbled across the same error under OSX Mavericks with an OSX app I'm developing, but unlike the other answers given, I definitely don't have any other threads interacting with the UI objects, and the view hierarchy in question is definitely visible too. I'm not using blocks either. Bizarrely the problem went away when I removed a vertical constraint on an NSTextField.

FWIW the problematic view whose removal from its superview causes the "internal error. Cannot find an outgoing row head for incoming head" error is one of many side-panel controls which together present the properties of objects in the main view which can be cut, copied, created etc. This means that the user can be pasting new objects into the main view quite rapidly, meaning the side-panel's controls are being destroyed and new ones created very rapidly, too. Of course, with everything in the main thread, this should not make a difference, but it seems to.

The exact constraint causing problems was

[self addConstraint: [NSLayoutConstraint constraintWithItem:control attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:other attribute:NSLayoutAttributeHeight multiplier:1.4 constant:0.0]];

where control is the (editable) NSTextField causing problems, and 'other' is another (non-editable) NSTextField label.

Johanajohanan answered 16/3, 2014 at 17:10 Comment(1)
This answer fixed it for me. I removed all constraints and views before dismissing and it works. Looks like some floating point math with a constraints was bugging out.Ankara
R
0

I got this problem with MZFormSheetController pod: https://github.com/m1entus/MZFormSheetController/issues/78

This code crashes:

[formSheetController.view addSubview:self.sharePanel];
// ...
[self.sharePanel removeFromSuperview]; // <-- CRASHES HERE

My solution is very strange but it works:

[self.sharePanel removeFromSuperview]; // <-- This line helps to avoid crash
[formSheetController.view addSubview:self.sharePanel];
// ...
[self.sharePanel removeFromSuperview];

And here is sharePanel property declaration:

@property (weak, nonatomic) IBOutlet UIView *sharePanel;
Renshaw answered 2/4, 2014 at 16:7 Comment(0)
M
0

I got this crash when I still have a missing constraint in wAnyhAny mode, fixing this removed the error.

Meeting answered 27/8, 2015 at 12:2 Comment(0)
D
0

As the other answers in this thread indicate this is somehow an invalid autolayout/contraint problem, though it appears to be very finicky about what qualifies as "invalid".

Luckily I hadn't made many changes since my last commit and was able to track down the offending changes. For me having a view of 10 horizontal UIImageView with equal width and fixed 2:3 aspect ratio was the issue.

The crash only seemed to occur after leaving the UIViewController that contained this image row. Each UIImageView was set to UIViewContentModeScaleAspectFill. Removing this content mode change (which was done before the UIImages were set) seemed to fix my problem but wasn't an acceptable solution. I ended up removing the aspect ratio restriction and just using a fixed width and height for each image.

Why this was crashing my application I don't know... The crash could also ONLY be reproduce on an iPhone 4s running iOS 7.1.2. I tried to reproduce the same crash on an iPhone 4s simulator running iOS 9.1 without success. It also wouldn't crash when running on a phsyical iPhone 5 running iOS 9.1.

Hope that helps someone out there

Delaminate answered 1/1, 2016 at 23:3 Comment(0)
E
0

According to Apple Documentation:

When developing for iOS 8.0 or later, set the constraint’s active property to YES instead of calling the addConstraint: method directly. The active property automatically adds and removes the constraint from the correct view.

In my case I had to modify the width constraint

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
            }
        }
Extraterritoriality answered 5/8, 2016 at 13:28 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.