Ios 具有`NSLayoutConstraint'的内存泄漏`

Ios 具有`NSLayoutConstraint'的内存泄漏`,ios,objective-c,memory-leaks,autolayout,Ios,Objective C,Memory Leaks,Autolayout,我有一个简单的布局,带有连接两个视图的约束,并将该约束存储在强属性中 我的期望是,在删除其中一个视图后,约束仍将存在,但它将变为非活动状态。但事实并非如此,我的应用程序因异常而崩溃 幸运的是,我能够重现这个问题。我附上视图控制器的源代码。您可以将其粘贴到使用“单视图应用程序”模板创建的新项目中 要再现问题,请在删除其中一个视图之前和之后打印约束描述。有一次,约束没有被移除,代码也正常工作(但只发生了一次)。通过调试器,我可以检查我删除的视图是否也没有解除分配,而它已经没有层,因此它的某些部分已经

我有一个简单的布局,带有连接两个视图的约束,并将该约束存储在强属性中

我的期望是,在删除其中一个视图后,约束仍将存在,但它将变为非活动状态。但事实并非如此,我的应用程序因异常而崩溃

幸运的是,我能够重现这个问题。我附上视图控制器的源代码。您可以将其粘贴到使用“单视图应用程序”模板创建的新项目中

要再现问题,请在删除其中一个视图之前和之后打印约束描述。有一次,约束没有被移除,代码也正常工作(但只发生了一次)。通过调试器,我可以检查我删除的视图是否也没有解除分配,而它已经没有层,因此它的某些部分已经解构。事情发生后,我注释掉了
printConstraintDescriptionButtouchedupInside
的实现,并在
RemoveViewButtonCouchedupInside
中设置断点。当调试器在第二次按下按钮后暂停应用程序时,约束不再存在

是否不允许持有对
NSLayoutConstraint
实例的强引用?我在文档中没有找到该信息

使用Xcode版本10.1(10B61)编译,运行iOS模拟器iPhone SE 12.1。在运行iOS 12.1.3(16D5032a)和11.2.2(15C202)的物理设备上,相同的逻辑(但不在独立代码段中)失败。使用Xcode 9.4.1版编译不会改变任何东西

ViewController.m

#import "ViewController.h"

@interface ViewController ()

@property (strong, nonatomic) UIView* topView;
@property (strong, nonatomic) UIView* bottomView;

@property (strong, nonatomic) NSLayoutConstraint* constraint;

@property (strong, nonatomic) UIButton* removeViewButton;
@property (strong, nonatomic) UIButton* printConstraintDescriptionButton;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.topView = [UIView new];
    self.topView.backgroundColor = [UIColor grayColor];
    self.topView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:self.topView];

    self.bottomView = [UIView new];
    self.bottomView.backgroundColor = [UIColor grayColor];
    self.bottomView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:self.bottomView];

    self.removeViewButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.removeViewButton.translatesAutoresizingMaskIntoConstraints = NO;
    [self.removeViewButton setTitle:@"Remove View"
                           forState:UIControlStateNormal];
    [self.removeViewButton addTarget:self
                              action:@selector(removeViewButtonTouchedUpInside)
                    forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.removeViewButton];

    self.printConstraintDescriptionButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.printConstraintDescriptionButton.translatesAutoresizingMaskIntoConstraints = NO;
    [self.printConstraintDescriptionButton setTitle:@"Print Constraint Description"
                                           forState:UIControlStateNormal];
    [self.printConstraintDescriptionButton addTarget:self
                                              action:@selector(printConstraintDescriptionButtonTouchedUpInside)
                                    forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.printConstraintDescriptionButton];

    NSDictionary* views = @{
                            @"topView": self.topView,
                            @"bottomView": self.bottomView,
                            @"removeViewButton": self.removeViewButton,
                            @"printConstraintDescriptionButton": self.printConstraintDescriptionButton
    };

    NSArray<NSString*>* constraints = @[
                             @"H:|-[topView]-|",
                             @"H:|-[bottomView]-|",
                             @"H:|-[printConstraintDescriptionButton]-|",
                             @"H:|-[removeViewButton]-|",
                             @"V:|-[topView(==44)]",
                             @"V:[bottomView(==44)]",
                             @"V:[printConstraintDescriptionButton]-[removeViewButton]-|"
    ];

    [constraints enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:obj
                                                                          options:0
                                                                          metrics:nil
                                                                            views:views]];
    }];

    self.constraint = [NSLayoutConstraint constraintWithItem:self.topView
                                                   attribute:NSLayoutAttributeBottom
                                                   relatedBy:NSLayoutRelationEqual
                                                      toItem:self.bottomView
                                                   attribute:NSLayoutAttributeTop
                                                  multiplier:1.0
                                                    constant:-8.0];
    self.constraint.active = YES;
}

- (void)printConstraintDescriptionButtonTouchedUpInside {
    NSLog(@"%@", self.constraint);
}

- (void)removeViewButtonTouchedUpInside {
    [self.bottomView removeFromSuperview];
    self.bottomView = nil;
}

@end
#导入“ViewController.h”
@界面视图控制器()
@属性(强,非原子)UIView*topView;
@属性(强,非原子)UIView*bottomView;
@属性(强,非原子)NSLayoutConstraint*约束;
@属性(强,非原子)UIButton*removeViewButton;
@属性(强,非原子)UIButton*printConstraintDescriptionButton;
@结束
@实现视图控制器
-(无效)viewDidLoad{
[超级视图下载];
self.topView=[UIView new];
self.topView.backgroundColor=[UIColor grayColor];
self.topView.translatesAutoResizengMaskintoConstraints=否;
[self.view addSubview:self.topView];
self.bottomView=[UIView new];
self.bottomView.backgroundColor=[UIColor grayColor];
self.bottomView.translatesAutoResizengmaskintoConstraints=否;
[self.view addSubview:self.bottomView];
self.removeViewButton=[UIButton Button类型:UIButtonTypeSystem];
self.removeViewButton.translatesAutoResizengMaskintoConstraints=否;
[self.removeViewButton集合标题:@“删除视图”
forState:uicontrol状态正常];
[self.removeViewButton添加目标:self
操作:@选择器(移除ViewButtonToucheDupInside)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.removeViewButton];
self.printConstraintDescriptionButton=[UIButton Button类型:UIButtonTypeSystem];
self.printConstraintDescriptionButton.translatesAutoReshizingMaskintoConstraints=否;
[self.printConstraintDescriptionButton集合标题:@“打印约束说明”
forState:uicontrol状态正常];
[self.printConstraintDescriptionButton添加目标:self
操作:@selector(PrintConstraintDescriptionButtontTouchedupInside)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.printConstraintDescriptionButton];
NSDictionary*视图=@{
@“topView”:self.topView,
@“bottomView”:self.bottomView,
@“removeViewButton”:self.removeViewButton,
@“printConstraintDescriptionButton”:self.printConstraintDescriptionButton
};
NSArray*约束=@[
@“H:|-[topView]-|”,
@“H:|-[bottomView]-|”,
@“H:|-[printConstraintDescriptionButton]-|”,
@“H:|-[removeViewButton]-|”,
@“V:|-[顶视图(=44)]”,
@“V:[底部视图(=44)]”,
@“V:[printConstraintDescriptionButton]-[removeViewButton]-|”
];
[constraints enumerateObjectsUsingBlock:^(NSString*\u非空obj,NSUInteger idx,BOOL*\u非空stop){
[self.view addConstraints:[NSLayoutConstraints Constraints WithVisualFormat:obj
选项:0
指标:零
视图:视图]];
}];
self.constraint=[NSLayoutConstraint constraintWithItem:self.topView
属性:NSLAYUTATTRIBUTEBOTTOM
关系人:NSLayoutRelationEqual
toItem:self.bottomView
属性:NSLAYUTATTRIBUTETOP
乘数:1.0
常数:-8.0];
self.constraint.active=是;
}
-(无效)打印约束描述按钮内部{
NSLog(@“%@”,自约束);
}
-(无效)将ViewButton从内部移除{
[self.bottomView从superview移除];
self.bottomView=nil;
}
@结束

问题不在于约束不再存在。。。问题在于:

NSLog(@"%@", self.constraint);
尝试打印约束的(默认)说明。如果约束未激活,则将抛出EXC_BAD_访问
- (void)printConstraintDescriptionButtonTouchedUpInside {

    NSLog(@"self.constraint still exists? %@", self.constraint != nil ? @"Yes" : @"No");

    NSLog(@"self.constraint.constant = %0.2f", self.constraint.constant);

    if (self.constraint.isActive) {
        NSLog(@"self.constraint (default description) %@", self.constraint);
    } else {
        // this will throw EXC_BAD_ACCESS exception if constraint is not active
        // so, don't do it
        //NSLog(@"self.constraint (default description) %@", self.constraint);
    }

}