Snapkit: Fatal error: updated constraint could not find existing matching constraint to update

Created on 26 Apr 2019  ·  16Comments  ·  Source: SnapKit/SnapKit

New Issue Checklist

  • [X] I have looked at the Documentation
  • [X] I have read the F.A.Q.
  • [X] I have filled out this issue template.

Issue Info

Info | Value |
-------------------------|-------------------------------------|
Platform | ios
Platform Version | 12.2
SnapKit Version | 5.0.0
Integration Method | cocoapods

Issue Description

After SnapKit update (5.0.0 was 4.2.0) I have now fatal error on updateConstraints

Capture d’écran 2019-04-26 à 19 19 31

Updated constraint could not find existing matching constraint to update

After some investigations I discovered where the issue is located: LayoutConstraint.swift

The object comparison between firstAnchor or secondAnchor doesn't work properly.

Capture d’écran 2019-04-26 à 18 53 52

I don't understand the purpose of #574 because firstItem is unowned(unsafe) meaning it'll crash if you try to update constraints for deallocated object

The real question is: Why are you trying to update constraints for deallocated objects 😄

Most helpful comment

@sashabelonogov, sure, it fails with margin properties, with topMargin for instance, but no crash with .top.

Simply use this sample code to test the crash:

import UIKit
import SnapKit

class ViewController: UIViewController {

  let snapView = UIView()

  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.addSubview(self.snapView)
    self.snapView.snp.makeConstraints { (make) in
      make.topMargin.equalToSuperview()
    }

    self.snapView.snp.updateConstraints { (make) in
      make.topMargin.equalToSuperview()
    }
  }
}

All 16 comments

The point of the https://github.com/SnapKit/SnapKit/pull/574 is that the object that the constraints are updated for is not deallocated and the EXC_BAD_ACCESS is raised when the firstItem or secondItem are accessed. The problem is fixed with https://github.com/SnapKit/SnapKit/pull/574. Please, follow Apple documentation about this properties:

     firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant
     Access to these properties is not recommended. Use the `firstAnchor` and `secondAnchor` properties instead.

It is ”not recommended by Apple”, so what? It's not deprecated and I join @arnauddorgans, this version completely broken my projects too, rollback to 4.2 needed.

Capture d’écran 2019-04-27 à 23 08 20

@bill350 @arnauddorgans I think @sashabelonogov's intentions were good so there's no need to be ill willed about this.

@sashabelonogov I believe the problem is that you're doing a === compare which compares the object pointers (invalid since NSLayoutAnchor is unique to each constraint) instead there is a weak var item: AnyObject inside NSLayoutAnchor that we can compare which from looks is identical to the old NSLayoutConstraint.item but weak so a nil value will not cause a crash.

@arnauddorgans @bill350 can either of you two check against develop and see if this solves your issue?

Sorry @robertjpayne if we were annoyed.
Fixes are always good intentions, but in this case the issue reproductibility was easy.

To avoid that in the future, we can add UI tests target on the SnapKit project ;)

Anyway, according to your reply, there are two things:

develop fixes not compile

I just tried to link the top commit c904582015d5a8eed917d79a7f28ea2b5a8ddde3 on the develop branch but I've got compile errors on an "item" property for NSLayoutAnchor 🤔. I think something is missing, or maybe you've mixed firstItem property?

  • Cocoapods 1.6.1
  • iOS 10 minimum target

image

NSLayoutAnchor introduction

I think the basis of the problem, with the merge of https://github.com/SnapKit/SnapKit/pull/574, is the introduction of the anchor system for the layout constraint comparaison, where NSLayoutAnchor seems to change after constraint creation.
I understand the Apple documentation that @sashabelonogov mentioned, but it's different things & levels for SnapKit equality comparaison.

Here an example between anchors vs items comparaison on a topMargin update (from 0 to 30):

| Anchor pointers comparaison (false) | Items pointers comparaison (true) |
|------|------|
| Capture d’écran 2019-04-28 à 12 38 42 | Capture d’écran 2019-04-28 à 12 40 06 |

Please check the @arnauddorgans sample too.

@bill350 thanks for your help, can you test again with latest commit? Sorry my time is extremely limited (the .item property is only available on macOS fun times…)

@robertjpayne, no pressure 🤗.
We have to take our time for this issue, with @sashabelonogov and @arnauddorgans too.
Create a dedicated branch will be better, I will find time for a PR tomorrow.
I can still have my project on Swift 5 and point to the 4.2 release of SnapKit in Swift 4.2 ;)

I've checked the commit and I still have a fatal error with the same sample of @arnauddorgans.

I don't understand why you are using anchor objects comparaison instead of items because it will simply not succeed. NSLayoutAnchor property will be different during the comparaison. Perhaps there is a difference between iOS and macOS layouting?

I've not developed macOS apps so far... I'm waiting for @sashabelonogov details too 🙂.

I will be able to dedicate some time to it to help with the issue.
I have an example that raises EXT_BAD_ACCESS randomly (every second/third time the application is opened) when the items are used (4.2.0 of SnapKit) The application works well with the changes applied in 5.0.0.
@bill350 or @arnauddorgans, does every updateConstraints raise this fatalError?
If not, could you please share an example to help reproduce it?

Thank you!

@sashabelonogov, sure, it fails with margin properties, with topMargin for instance, but no crash with .top.

Simply use this sample code to test the crash:

import UIKit
import SnapKit

class ViewController: UIViewController {

  let snapView = UIView()

  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.addSubview(self.snapView)
    self.snapView.snp.makeConstraints { (make) in
      make.topMargin.equalToSuperview()
    }

    self.snapView.snp.updateConstraints { (make) in
      make.topMargin.equalToSuperview()
    }
  }
}

Sound like xxMargin (xx: top, left, right, bottom)as a computed property, created every time when accessed it. Using === is not the right way. There is no problem when updating only top, left, right, bottom. Also, it does not work when using as the following way,

self.snapView.snp.makeConstraints { (make) in
  make.top.equalTo(self.view.topMargin)
}
self.snapView.snp.updateConstraints { (make) in
  make.top.equalTo(self.view.topMargin)
}

Any updates on this regression @sashabelonogov ?
Did you check the sample code?

Has this problem been resolved?

No. We just raised the regression about margins update crash, but no strong investigation started to fix it (simply rollback).

I'm looking into this again today, I'm really sorry for the major delay just been super busy.

This is now resolved with release of 5.0.1, the solution was an entire rollback on #574. @arnauddorgans if you can provide a more concrete example of what is causing this bug we can possibly work towards a fix.

The main issue is that SnapKit has no other way to compare constraints (NSLayoutAnchor is simply just not equatable and does not have public API's suitable for comparing).

My guess is your main issue is caused by you losing references to NSLayoutGuide's or NSLayoutAnchor's and then attempting an update constraint.

v5.0.1 Has this problem been resolved?
1441596172892_ pic_hd
1451596172924_ pic_hd

Was this page helpful?
0 / 5 - 0 ratings