菜鸟教程小白 发表于 2022-12-11 22:08:11

ios - 内存泄漏 `NSLayoutConstraint`


                                            <p><p>我有一个简单的布局,其中包含连接两个 View 的约束,我将该约束存储在一个强属性中。</p>

<p>我的期望是,在删除其中一个 View 后,约束仍然存在,但它会变为非事件状态。但事实并非如此,我的应用程序因 <code>EXC_BAD_ACCESS</code> 异常而崩溃。</p>

<p>幸运的是,我能够重现该问题。我附上 ViewController 的源代码。您可以将其粘贴到使用“Single View App”模板创建的新项目中。</p>

<p>要重现问题,请在删除其中一个 View 之前和之后打印约束描述。它发生在我身上一次,约束没有被删除并且代码有效(但它只发生了一次)。使用调试器,我能够检查我删除的 View 是否也没有被释放,而它已经没有层,所以它的某些部分已经被解构。在它发生之后,我注释掉了 <code>printConstraintDescriptionButtonTouchedUpInside</code> 的实现,并在 <code>removeViewButtonTouchedUpInside</code> 中设置了断点。当调试器在第二次按下按钮后暂停应用时,约束不再存在。</p>

<p>是否不允许对 <code>NSLayoutConstraint</code> 实例进行强引用?我没有在文档中找到该信息。</p>

<p>使用 Xcode 版本 10.1 (10B61) 编译,运行 iOS Simulator iPhone SE 12.1。在运行 iOS 12.1.3 (16D5032a) 和 11.2.2 (15C202) 的物理设备上,相同的逻辑但在隔离片段中没有失败。使用 Xcode 版本 9.4.1 进行编译不会改变任何内容。</p>

<p><strong>ViewController.m</strong></p>

<pre><code>#import &#34;ViewController.h&#34;

@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 {
    ;

    self.topView = ;
    self.topView.backgroundColor = ;
    self.topView.translatesAutoresizingMaskIntoConstraints = NO;
    ;

    self.bottomView = ;
    self.bottomView.backgroundColor = ;
    self.bottomView.translatesAutoresizingMaskIntoConstraints = NO;
    ;

    self.removeViewButton = ;
    self.removeViewButton.translatesAutoresizingMaskIntoConstraints = NO;
    [self.removeViewButton setTitle:@&#34;Remove View&#34;
                           forState:UIControlStateNormal];
    [self.removeViewButton addTarget:self
                              action:@selector(removeViewButtonTouchedUpInside)
                  forControlEvents:UIControlEventTouchUpInside];
    ;

    self.printConstraintDescriptionButton = ;
    self.printConstraintDescriptionButton.translatesAutoresizingMaskIntoConstraints = NO;
    [self.printConstraintDescriptionButton setTitle:@&#34;Print Constraint Description&#34;
                                           forState:UIControlStateNormal];
    [self.printConstraintDescriptionButton addTarget:self
                                              action:@selector(printConstraintDescriptionButtonTouchedUpInside)
                                    forControlEvents:UIControlEventTouchUpInside];
    ;

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

    NSArray&lt;NSString*&gt;* constraints = @[
                           @&#34;H:|--|&#34;,
                           @&#34;H:|--|&#34;,
                           @&#34;H:|--|&#34;,
                           @&#34;H:|--|&#34;,
                           @&#34;V:|-&#34;,
                           @&#34;V:&#34;,
                           @&#34;V:--|&#34;
    ];

    [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(@&#34;%@&#34;, self.constraint);
}

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

@end
</code></pre></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>问题不在于约束不再存在……问题在于:</p>

<pre><code>NSLog(@&#34;%@&#34;, self.constraint);
</code></pre>

<p>尝试打印约束的(默认)<strong><em>描述</em></strong>。如果约束未激活,则会抛出 EXC_BAD_ACCESS 异常(您无法使用 @try/catchblock 捕获)。</p>

<p>用这个替换你的方法:</p>

<pre><code>- (void)printConstraintDescriptionButtonTouchedUpInside {

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

    NSLog(@&#34;self.constraint.constant = %0.2f&#34;, self.constraint.constant);

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

}
</code></pre>

<p>您将看到前两个 <code>NSLog()</code> 执行良好,无论约束是否处于事件状态。但是,如果您删除 <code>bottomView</code>(<s> 或以其他方式停用 <code>self.constraint</code></s>),如果您尝试访问 <code>self,则会出现错误.constraint.description</code>(或<code>self.constraint.debugDescription</code>)。</p></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 内存泄漏`NSLayoutConstraint`,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/54635509/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/54635509/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 内存泄漏 `NSLayoutConstraint`