我有一个简单的布局,带有一个连接两个视图的约束,并将该约束存储在一个强大的属性中。
我的期望是,在删除这些视图之一之后,约束将仍然存在,但是它将变为非活动状态。但事实并非如此,我的应用因EXC_BAD_ACCESS
异常而崩溃。
幸运的是,我能够重现该问题。我附上了视图控制器的源代码。您可以将其粘贴到使用“ Single View App”模板创建的新项目中。
要重现该问题,请在删除其中一个视图之前和之后打印约束描述。在我身上发生了一次,没有删除约束并且代码起作用了(但是只发生了一次)。使用调试器,我可以检查删除的视图是否也没有解除分配,尽管它没有图层,所以其中的某些部分已经被解构。发生之后,我注释了printConstraintDescriptionButtonTouchedUpInside
的实现,只是在removeViewButtonTouchedUpInside
中设置了断点。当调试器第二次按下按钮后暂停应用程序时,约束不再存在。
是否不允许强烈引用NSLayoutConstraint
实例?我尚未在文档中找到该信息。
已与运行iOS Simulator iPhone SE 12.1的Xcode版本10.1(10B61)一起编译。在运行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
答案 0 :(得分:0)
问题不在于约束不再存在...问题在于:
NSLog(@"%@", self.constraint);
尝试打印约束的(默认) 描述 。如果约束不处于活动状态,则将引发EXC_BAD_ACCESS异常(您无法使用@ try / catch块捕获该异常)。
为此替换您的方法:
- (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);
}
}
无论约束是否处于活动状态,您都会看到前两个NSLog()
执行得很好。但是,如果删除bottomView
(或以其他方式停用),则尝试访问self.constraint
self.constraint.description
(或self.constraint.debugDescription
)。