Why using NSLayoutConstraint.activate is more efficient than .isActive = true?

Viewed 287

My question is about the creation of constraints: why creating an array of constraints to be activated later with the activate method is more efficient than activating each one by one with .isActive = true?

Activating a direct constraint, we're basically changing an attribute, a variable that's already instantiated in the UIView we're tinkering with:

@available(iOS 9.0, *)
open var topAnchor: NSLayoutYAxisAnchor { get }

But the definition shows that using the Array is more efficient:

/* Convenience method that activates each constraint in the contained array, in the same manner as setting active=YES.
This is often more efficient than activating each constraint individually. */
@available(iOS 8.0, *)
open class func activate(_ constraints: [NSLayoutConstraint])

I don't see how to create an array of a variable that is already in scope and then pass it in a loop to activate it. How is this more efficient than enabling direct constraint?

1 Answers

When you have multiple constraints you need to activate, AutoLayout has no idea when you're going to be "done" activating them if you go one by one. This might lead it to perform some intermediate calculations between constraints, whose results are wasted by adding another constraint immediately afterwards. By providing AutoLayout a list of constraints at once, it can both make sure it doesn't perform any intermediate calculations in between activations, and can get a better idea of the changes being made, leading to potentially more accurate and performant calculations.


If you poke around in /System/Library/PrivateFrameworks/CoreAutoLayout.framework with a disassembler like Hopper, you might (currently) see:

  • The implementation of +[NSAutolayoutConstraint activateConstraints:] is a passthrough to the private +[NSAutolayoutConstraint _addOrRemoveConstraints:activate:]
  • +[NSAutolayoutConstraint _addOrRemoveConstraints:activate:], for each constraint, tries to get a reference to the layout engine that the constraint belongs to (-[NSLayoutConstraint _layoutEngine]), and
    • If it belongs to an engine, activates or deactivates the constraint by calling through to the engine's -[NSISEngine withAutomaticOptimizationsDisabled:] method
    • If it doesn't yet belong to an engine, it will activate the constraint directly

-[NSLayoutConstraint setActive:] itself also does a non-trivial amount of work, potentially searching for its _nearestAncestorLayoutItem to _findCommonAncestorOfItem:andItem: in order to insert into the right place. By doing this work inside of a context that has optimizations disabled, autolayout can avoid doing that unnecessary work for each and every one of the constraints in the list.


The amount of work that AutoLayout has to do for each of your constraints likely dwarfs the cost of creating an array to store them temporarily, though you can likely profile this in a live app to see what it might be costing you.

Related