【问题标题】:Why is addSubview: not retaining the view?为什么 addSubview: 不保留视图?
【发布时间】:2012-03-17 03:32:24
【问题描述】:

我正在向我的视图添加两个由属性存储的子视图。将子视图添加到我的视图时,子视图似乎在我的 setup 方法被调用后被释放。最终结果是视图永远不会显示。现在,如果我将我的属性更改为strong 而不是weak,我会保留对视图的引用,它们现在会显示在屏幕上。那么这里发生了什么?为什么addSubview:insertSubview: 不保留子视图?请看下面的代码:

顺便说一句,我正在使用带有 ARC 的 iOS5(因此有强和弱的东西)

#import "NoteView.h"
@interface NoteView() <UITextViewDelegate>
@property (weak, nonatomic) HorizontalLineView *horizontalLineView;  // custom subclass of UIView that all it does is draw horizontal lines
@property (weak, nonatomic) UITextView *textView;
@end

@implementation NoteView
@synthesize horizontalLineView = _horizontalLineView;
@synthesize textView = _textView;

#define LEFT_MARGIN 20
- (void)setup
{
    // Create the subviews and set the frames
    self.horizontalLineView = [[HorizontalLineView alloc] initWithFrame:self.frame];
    CGRect textViewFrame = CGRectMake(LEFT_MARGIN, 0, self.frame.size.width, self.frame.size.height);
    self.textView = [[UITextView alloc] initWithFrame:textViewFrame];


    // some addition setup stuff that I didn't include in this question...

    // Finally, add the subviews to the view
    [self addSubview:self.textView];
    [self insertSubview:self.horizontalLineView atIndex:0];
}

- (void)awakeFromNib
{
    [super awakeFromNib];

    [self setup];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        [self setup];
    }
    return self;
}

【问题讨论】:

  • 你是如何创建 NoteView 的实例的?在设置中看到控制权..
  • 我正在通过将 NoteView 实例添加到情节提要中的视图控制器来创建它。
  • 你的课是在 Xib 中指定为 NoteView

标签: iphone objective-c ios cocoa-touch uiview


【解决方案1】:

这是您的代码:

self.horizontalLineView = [[HorizontalLineView alloc] initWithFrame:self.frame];

回想一下horizontalLineView 属性很弱。让我们通过 ARC 生成的额外代码来了解该行中真正发生的事情。首先,发送allocinitWithFrame: 方法,获取强引用:

id temp = [[HorizontalLineView alloc] initWithFrame:self.frame];

此时,HorizontalLineView 对象的保留计数为 1。接下来,由于您使用点语法设置了 horizontalLineView 属性,因此编译器生成代码以将 setHorizontalLineView: 方法发送到 self ,将temp 作为参数传递。由于HorizontalLineView 属性被声明为weak,setter 方法会这样做:

objc_storeWeak(&self->_horizontalLineView, temp);

这将设置self-&gt;_horizontalLineView 等于temp,并将&amp;self-&gt;_horizontalLineView 放在对象的弱引用列表中。但它确实不会增加HorizontalLineView 对象的保留计数。

最后,因为不再需要temp 变量,编译器会生成这个:

[temp release];

这会将HorizontalLineView 对象的保留计数降低到零,因此它会释放该对象。在释放期间,它遍历弱引用列表,并将每个引用设置为nil。所以self-&gt;_horizontalLineView 变成了nil

解决此问题的方法是使 temp 变量显式化,这样您就可以延长它的生命周期,直到您将 HorizontalLineView 对象添加到它的父视图之后,它会保留它:

HorizontalLineView *hlv = [[HorizontalLineView alloc] initWithFrame:self.frame];
self.horizontalLineView = hlv;
// ...
[self insertSubview:hlv atIndex:0];

【讨论】:

  • 哇——你说得对!这正是我希望对幕后发生的事情做出的确切解释。尽管您必须完成创建显式变量的额外步骤,但这似乎不奇怪吗?无论如何,感谢您的帮助!
  • 我发现只需引入一个临时变量就足够了,你不需要在调用insertSubview时使用它。所以你可以使用[self insertSubview:self.horizontalLineView atIndex:0];,它仍然有效。这对我来说似乎违反直觉。你能解释一下这种行为吗?
【解决方案2】:

Weak 不应该使用,除非在形成父子保留循环的情况下(父保留对子的引用,子保留对父的引用,因此两者都不是 dealloc'd)。 Strong 是 ARC 的等价物(现在在 ARC 下无效),并且比弱引用更长时间地保持一个稳定的指向对象的指针,因此 addSubview 实际上有效,而不是给你某种错误。

为什么 addSubview: 和 insertSubview: 不保留子视图?

你有没有试过在一个 nil 对象上调用 retain?是的,还是零。您的弱引用没有使 UIView 对象保持足够长的时间以成功地在对象上“调用保留”。

【讨论】:

  • 我的意思是我明白你所说的视图可能不会被保留足够长的时间来保留,但是应该在实例化(父)视图之后调用 setup 方法。所以当我将我的子视图添加到视图层次结构中时,它们会被保留,对吗?所以我想我在这里仍然很困惑。我错过了什么?
  • ... Rob,我认为它不会崩溃 - 你只是向 nil 发送消息,所以应该没问题。
  • 已编辑。只是有点困惑而已。谢谢 Rob 和 Jake。
【解决方案3】:

尝试改用这个:

    @property (nonatomic, retain)

【讨论】:

  • 弧下保留无效,求解释。
  • @CodaFi,保留属性在 ARC 下完全有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-02
  • 1970-01-01
  • 2015-01-23
  • 2011-05-09
  • 2014-09-07
  • 2018-01-02
  • 2023-04-04
相关资源
最近更新 更多