У меня есть простой макет с ограничением, соединяющим два представления, и я сохраняю это ограничение в сильном свойстве.
Я ожидаю, что после удаления одного из этих представлений ограничение все еще будет существовать, но оно будетвыключитьНо это не тот случай, и мое приложение вылетает с исключением EXC_BAD_ACCESS
.
К счастью, я могу воспроизвести проблему.Я прилагаю исходный код контроллера представления.Вы можете просто вставить его в новый проект, созданный с помощью шаблона «Single View App».
Чтобы воспроизвести проблему, напечатайте описание ограничения до и после удаления одного из представлений.Однажды со мной случилось, что ограничение не было снято, и код работал (но это случилось только один раз).С помощью отладчика я смог проверить, что удаленное представление также не было освобождено, хотя у него уже не было слоя, поэтому некоторые его части уже были деконструированы.После того, как это произошло, я прокомментировал реализацию 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
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