Uploaded image for project: 'Swift'
  1. Swift
  2. SR-1647

Compiler hangs on large array of NSLayoutConstraints, with a type annotation

    Details

      Description

      I have an project without storyboard.
      All Compounds are arrenged by constraints.

      On Swift 1 I have set all constraints in one big array an set it, with one addConstraints() call

      On Swift 2.2 the Compiler will hang and was killed after 8 hours without any useful error message

      As workaround a have found, that I need to set the Constraints on

      Here is the function which let the compiler hangs:

      func setConstraints()
          {
              
              var constraints:[NSLayoutConstraint] = [
                  
                  
                  NSLayoutConstraint(item: positiveLabel,attribute: NSLayoutAttribute.Top,relatedBy: NSLayoutRelation.Equal,toItem: view,attribute: NSLayoutAttribute.Top,multiplier: 1,constant: 0),
                  
                  NSLayoutConstraint(item:positiveLabel,attribute: NSLayoutAttribute.Left,relatedBy: NSLayoutRelation.Equal,toItem: view,attribute: NSLayoutAttribute.Left,multiplier: 1,constant: 0),
                  
                  NSLayoutConstraint(item:positiveLabel,attribute: NSLayoutAttribute.Width,relatedBy: NSLayoutRelation.Equal,toItem: nil,attribute: NSLayoutAttribute.NotAnAttribute,multiplier: 1,constant: 50),
                  /*
                  ]
              view.addConstraints(constraints)
              constraints = [
       */
                  NSLayoutConstraint(item: positiveBox,attribute: NSLayoutAttribute.CenterY,relatedBy: NSLayoutRelation.Equal,toItem: positiveLabel,attribute: NSLayoutAttribute.CenterY,multiplier: 1,constant: 0),
                  
                  NSLayoutConstraint(item:positiveBox,attribute: NSLayoutAttribute.Left,relatedBy: NSLayoutRelation.Equal,toItem: positiveLabel,attribute: NSLayoutAttribute.Right,multiplier: 1,constant: 10),
                  
                  NSLayoutConstraint(item:positiveBox,attribute: NSLayoutAttribute.Height,relatedBy: NSLayoutRelation.Equal,toItem: positiveLabel,attribute: NSLayoutAttribute.Height,multiplier: 0.8,constant: 0),
                  
                  NSLayoutConstraint(item:positiveBox,attribute: NSLayoutAttribute.Width,relatedBy: NSLayoutRelation.Equal,toItem: positiveBox,attribute: NSLayoutAttribute.Height,multiplier: 1,constant: 0),
         /*
              ]
              view.addConstraints(constraints)
              
              
              constraints = [
           */
                  NSLayoutConstraint(item:negativeLabel,attribute: NSLayoutAttribute.Top,relatedBy: NSLayoutRelation.Equal,toItem: positiveLabel,attribute: NSLayoutAttribute.Bottom,multiplier: 1,constant: 0),
                  NSLayoutConstraint(item:negativeLabel,attribute: NSLayoutAttribute.Left,relatedBy: NSLayoutRelation.Equal,toItem: view,attribute: NSLayoutAttribute.Left,multiplier: 1,constant: 0),
                  NSLayoutConstraint(item:negativeLabel,attribute: NSLayoutAttribute.Width,relatedBy: NSLayoutRelation.Equal,toItem: nil,attribute: NSLayoutAttribute.NotAnAttribute,multiplier: 1,constant: 50),
             /*
                  ]
              view.addConstraints(constraints)
              constraints = [
       */
                  NSLayoutConstraint(item:negativeBox,attribute: NSLayoutAttribute.CenterY,relatedBy: NSLayoutRelation.Equal,toItem: negativeLabel,attribute: NSLayoutAttribute.CenterY,multiplier: 1,constant: 0),
                  NSLayoutConstraint(item:negativeBox,attribute: NSLayoutAttribute.Left,relatedBy: NSLayoutRelation.Equal,toItem: negativeLabel,attribute: NSLayoutAttribute.Right,multiplier: 1,constant: 10),
                  NSLayoutConstraint(item:negativeBox,attribute: NSLayoutAttribute.Height,relatedBy: NSLayoutRelation.Equal,toItem: negativeLabel,attribute: NSLayoutAttribute.Height,multiplier: 0.8,constant: 0),
                  NSLayoutConstraint(item:negativeBox,attribute: NSLayoutAttribute.Width,relatedBy: NSLayoutRelation.Equal,toItem: negativeBox,attribute: NSLayoutAttribute.Height,multiplier: 1,constant: 0),
         /*
              ]
              view.addConstraints(constraints)
              
              
              constraints = [
       */
                  NSLayoutConstraint(item:neutralLabel,attribute: NSLayoutAttribute.Top,relatedBy: NSLayoutRelation.Equal,toItem: negativeLabel,attribute: NSLayoutAttribute.Bottom,multiplier: 1,constant: 0),
                  NSLayoutConstraint(item:neutralLabel,attribute: NSLayoutAttribute.Left,relatedBy: NSLayoutRelation.Equal,toItem: view,attribute: NSLayoutAttribute.Left,multiplier: 1,constant: 0),
                  NSLayoutConstraint(item:neutralLabel,attribute: NSLayoutAttribute.Width,relatedBy: NSLayoutRelation.Equal,toItem: nil,attribute: NSLayoutAttribute.NotAnAttribute,multiplier: 1,constant: 50),
         /*
                  ]
              view.addConstraints(constraints)
              constraints = [
       */
                  NSLayoutConstraint(item:neutralBox,attribute: NSLayoutAttribute.CenterY,relatedBy: NSLayoutRelation.Equal,toItem: neutralLabel,attribute: NSLayoutAttribute.CenterY,multiplier: 1,constant: 0),
                  NSLayoutConstraint(item:neutralBox,attribute: NSLayoutAttribute.Left,relatedBy: NSLayoutRelation.Equal,toItem: neutralLabel,attribute: NSLayoutAttribute.Right,multiplier: 1,constant: 10),
                  NSLayoutConstraint(item:neutralBox,attribute: NSLayoutAttribute.Height,relatedBy: NSLayoutRelation.Equal,toItem: neutralLabel,attribute: NSLayoutAttribute.Height,multiplier: 0.8,constant: 0),
                  NSLayoutConstraint(item:neutralBox,attribute: NSLayoutAttribute.Width,relatedBy: NSLayoutRelation.Equal,toItem: neutralBox,attribute: NSLayoutAttribute.Height,multiplier: 1,constant: 0),
              ]
              view.addConstraints(constraints)
      
      
      }
      

      The short project to show the Error can be found here:

      https://github.com/tn0/swiftError.git

        Attachments

          Activity

          Hide
          ayaka Ayaka Nonaka added a comment -

          I’ve run into this issue multiple times as well. Happened to me with a view that has only three subviews, and just 12 constraints. My workaround was to split the activation out into multiple calls to activateConstraints, but initially it was not apparently why the compile time got so slow. I deleted derived data, restarted Xcode, restarted computer before I realized that it was the activateConstraints call that was causing the issue.

          I’d love to help track down the root issue if anyone with more compiler knowledge could also take a look!

          Show
          ayaka Ayaka Nonaka added a comment - I’ve run into this issue multiple times as well. Happened to me with a view that has only three subviews, and just 12 constraints. My workaround was to split the activation out into multiple calls to activateConstraints, but initially it was not apparently why the compile time got so slow. I deleted derived data, restarted Xcode, restarted computer before I realized that it was the activateConstraints call that was causing the issue. I’d love to help track down the root issue if anyone with more compiler knowledge could also take a look!
          Hide
          ayaka Ayaka Nonaka added a comment - - edited

          This is still a huge issue in Swift 3.0. It is unfortunate because the alternative is to use isActive = true instead, which is way faster for compiling but slower at runtime and UI performance(1). Let me know how I can help track this issue down, because it’s not ideal to have to trade off between compile time and performance at runtime.

          1. from WWDC 2015 Session 219 (http://asciiwwdc.com/2015/sessions/219)

          "It turns out that changing a constraint inside update constraints is actually faster than changing a constraint at other times.

          The reason for that is because the engine is able to treat all the constraint changes that happen in this pass as a batch.

          This is the same kind of performance benefit that you get by calling activate constraints on an entire array of constraints as opposed to activating each of those constraints individually."

          Show
          ayaka Ayaka Nonaka added a comment - - edited This is still a huge issue in Swift 3.0. It is unfortunate because the alternative is to use isActive = true instead, which is way faster for compiling but slower at runtime and UI performance(1). Let me know how I can help track this issue down, because it’s not ideal to have to trade off between compile time and performance at runtime. 1. from WWDC 2015 Session 219 ( http://asciiwwdc.com/2015/sessions/219 ) "It turns out that changing a constraint inside update constraints is actually faster than changing a constraint at other times. The reason for that is because the engine is able to treat all the constraint changes that happen in this pass as a batch. This is the same kind of performance benefit that you get by calling activate constraints on an entire array of constraints as opposed to activating each of those constraints individually."
          Hide
          jrose Jordan Rose added a comment -

          I don't think that's fair. append gets the same behavior without multiple calls to addConstraints.

          Show
          jrose Jordan Rose added a comment - I don't think that's fair. append gets the same behavior without multiple calls to addConstraints .
          Hide
          ayaka Ayaka Nonaka added a comment - - edited

          Jordan Rose Thanks for your reply. Yes we ended up converting almost all of our AL code to `append` after we realized we could do that instead. It would still be nice to be able to pass in an array directly like

          `NSLayoutConstraint.activate([NSLayoutConstraint(...), NSLayoutConstraint(...), ...])`

          mostly for readability and also to prevent bugs from forgetting to call `NSLayoutConstraint.activate(theArrayWeWereAppendingTo)` because that was I was most afraid of forgetting while refactoring the code to use `append` instead.

          Show
          ayaka Ayaka Nonaka added a comment - - edited Jordan Rose Thanks for your reply. Yes we ended up converting almost all of our AL code to `append` after we realized we could do that instead. It would still be nice to be able to pass in an array directly like `NSLayoutConstraint.activate( [NSLayoutConstraint(...), NSLayoutConstraint(...), ...] )` mostly for readability and also to prevent bugs from forgetting to call `NSLayoutConstraint.activate(theArrayWeWereAppendingTo)` because that was I was most afraid of forgetting while refactoring the code to use `append` instead.
          Hide
          robnapier Rob Napier added a comment - - edited

          Adding an extra initializer to NSLayoutConstraint fixes this. I believe the problem is the Any? in particular. If you can remove that, things are dramatically faster (basically instant). For example:

          extension NSLayoutConstraint {
              public convenience init(item view1: AnyObject, attribute attr1: NSLayoutAttribute, relatedBy relation: NSLayoutRelation, toItem view2: AnyObject?, attribute attr2: NSLayoutAttribute, multiplier: CGFloat, constant c: CGFloat) {
                  self.init(item: view1 as Any, attribute: attr1, relatedBy: relation, toItem: view2 as Any?, attribute: attr2, multiplier: multiplier, constant: c)
              }
          }
          

          Here I've changed view1 and view2 to be AnyObject and AnyObject? rather than Any and Any? (and use as to make sure I call the original version). Similar improvements occur if you make them UIView and UIView? or even just both Any (so there is no Any?). Basically the entire problem is in the Any?.

          (In Swift 2.2, these were AnyObject and it also had a problem. I suspect adding a non-optional version would fix 2.2, but I haven't tested that.)

          Show
          robnapier Rob Napier added a comment - - edited Adding an extra initializer to NSLayoutConstraint fixes this. I believe the problem is the Any? in particular. If you can remove that, things are dramatically faster (basically instant). For example: extension NSLayoutConstraint { public convenience init(item view1: AnyObject, attribute attr1: NSLayoutAttribute, relatedBy relation: NSLayoutRelation, toItem view2: AnyObject?, attribute attr2: NSLayoutAttribute, multiplier: CGFloat, constant c: CGFloat) { self.init(item: view1 as Any, attribute: attr1, relatedBy: relation, toItem: view2 as Any?, attribute: attr2, multiplier: multiplier, constant: c) } } Here I've changed view1 and view2 to be AnyObject and AnyObject? rather than Any and Any? (and use as to make sure I call the original version). Similar improvements occur if you make them UIView and UIView? or even just both Any (so there is no Any? ). Basically the entire problem is in the Any? . (In Swift 2.2, these were AnyObject and it also had a problem. I suspect adding a non-optional version would fix 2.2, but I haven't tested that.)
          Hide
          ruddct Rudd T added a comment - - edited

          Confirming that Rob Napier's extension fixes this issue nicely, and that this bug still exists as of Xcode Version 8.1 (8T61a)

          Show
          ruddct Rudd T added a comment - - edited Confirming that Rob Napier 's extension fixes this issue nicely, and that this bug still exists as of Xcode Version 8.1 (8T61a)

            People

            • Assignee:
              rudkx Mark Lacey
              Reporter:
              Nowak Thomas
            • Votes:
              5 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

              • Created:
                Updated: