我有一个简单的布局,其中包含连接两个 View 的约束,我将该约束存储在一个强属性中。
我的期望是,在删除其中一个 View 后,约束仍然存在,但它会变为非事件状态。但事实并非如此,我的应用程序因 EXC_BAD_ACCESS 异常而崩溃。
幸运的是,我能够重现该问题。我附上 View Controller 的源代码。您可以将其粘贴到使用“Single View App”模板创建的新项目中。
要重现问题,请在删除其中一个 View 之前和之后打印约束描述。它发生在我身上一次,约束没有被删除并且代码有效(但它只发生了一次)。使用调试器,我能够检查我删除的 View 是否也没有被释放,而它已经没有层,所以它的某些部分已经被解构。在它发生之后,我注释掉了 printConstraintDescriptionButtonTouchedUpInside 的实现,并在 removeViewButtonTouchedUpInside 中设置了断点。当调试器在第二次按下按钮后暂停应用时,约束不再存在。
是否不允许对 NSLayoutConstraint 实例进行强引用?我没有在文档中找到该信息。
使用 Xcode 版本 10.1 (10B61) 编译,运行 iOS Simulator 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
actionselector(removeViewButtonTouchedUpInside)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.removeViewButton];
self.printConstraintDescriptionButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.printConstraintDescriptionButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.printConstraintDescriptionButton setTitle"rint Constraint Description"
forState:UIControlStateNormal];
[self.printConstraintDescriptionButton addTarget:self
actionselector(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 constraintsWithVisualFormatbj
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
Best Answer-推荐答案 strong>
问题不在于约束不再存在……问题在于:
NSLog(@"%@", self.constraint);
尝试打印约束的(默认)描述。如果约束未激活,则会抛出 EXC_BAD_ACCESS 异常(您无法使用 @try/catch block 捕获)。
用这个替换你的方法:
- (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 )。
关于ios - 内存泄漏 `NSLayoutConstraint`,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/54635509/
|