【问题标题】:iPhone Memory Management and ReleasingiPhone 内存管理和释放
【发布时间】:2009-05-05 02:03:51
【问题描述】:

这是我经常看到的一种常见做法(包括来自非常流行的 iPhone 开发者书籍)

在.h文件中:

@interface SomeViewController : UIViewController
{
  UIImageView *imgView;
}

.m 文件中的某处:

imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
applicationFrame]];
[imgView setImage:[UIImage imageNamed:@"someimage.png"]];
[self addSubview:imgView];
[imgView release];

后来,我们看到了……

- (void) dealloc
{
  [imgView release];
  [super dealloc];

} 

既然imgView有一个匹配的alloc和release,那么dealloc中imgView的release有必要吗?

addSubview调用保留的imgView在哪里占?

【问题讨论】:

    标签: iphone objective-c cocoa memory-management nsview


    【解决方案1】:

    代码不正确。 imgView 被释放后,你最终会释放它。

    在您的 .m 文件中,您:

    1. alloc 它 --> 你拥有它
    2. 将其添加为子视图 --> 你和the UIView owns it
    3. release 它 --> 你不拥有它

    然后在dealloc,你release imgView 即使,正如我们在上面的步骤 3 中建立的,你不拥有它。当你调用[super dealloc] 时,视图会释放它所有的子视图,我想你会得到一个异常。

    如果您想保留imgView 的ivar,我建议不要在将其添加为子视图后调用release,并保持dealloc 不变。这样,即使 imgView 在某个时刻从视图层次结构中删除,您仍然可以对它进行有效引用。

    【讨论】:

      【解决方案2】:

      代码不正确,您不应该在调用 dealloc 时在 init 方法中释放它(如果您想将其保留为 ivar,则不需要,除非您需要指向它的指针其他地方,因为 addSubview: 将为您保留视图)。

      我相信它实际上没有崩溃的原因是它仍然被超类保留(来自对 addSubview 的调用:),所以当它在 dealloc 中释放时,实际上已经平衡了。该视图可能会在其被释放后立即从父视图中删除,因此当[super dealloc] 被调用时,它不会被过度释放。这是我的预感,至少。

      【讨论】:

        【解决方案3】:

        init 中的发布不正确。

        您提到了“常见做法”和一本未命名的书。我建议查看 Apple 的规范示例:ViewTransitions 是这种情况下的一个很好的示例(以及 2 个要启动的视图;)

        http://developer.apple.com/iphone/library/samplecode/ViewTransitions/index.html

        【讨论】:

          【解决方案4】:

          (我还没有足够的声望添加评论。)

          @bentford:如果我错了,请纠正我,但我相信为了使用 imgView 属性的综合设置器,你必须使用“self.imgView”:

          self.imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
          

          如果你没有self.,它只是在使用ivar,并没有获得额外的retain。

          【讨论】:

          • 我想我很困惑。我删除了我的答案,因为它没有帮助。感谢您的反馈。
          【解决方案5】:

          基本答案是,示例代码中应该只有一个[imgView release]无论是在 addSubview 之后还是在 dealloc 中)。但是,我会从dealloc 中删除[imgView release],并在addSubview 之后保留它。

          iPhone 上有一个问题;使用didReceiveMemoryWarning,您可以从您的下方释放对象(包括整个视图)。如果您有一个应用程序范围的保留集并且您不尊重内存,那么您可能会发现应用程序只是被杀死了。

          一个很好的例子是:
          如果您想到一组嵌套的 3 个视图,视图 1-> 视图 2-> 视图 3。 接下来,考虑“viewDidLoad”和“viewDidUnload”调用。如果用户当前处于“视图 3”中,则视图 1 可能已卸载,这就是它变得令人讨厌的地方。
          如果您在viewDidLoad 中分配了一个对象,并且在将其添加到子视图后没有释放它,那么在卸载view1 时您的对象没有释放,但是仍然卸载了view1。
          viewDidLoad 将再次运行您的代码将再次运行,但现在您的对象有两个实例而不是一个;一个对象将位于先前未加载的视图的无处,而新对象将用于当前可见的视图。冲洗、起泡并重复,您会发现您的应用程序因内存泄漏而崩溃。

          在此示例中,如果给定的代码块是易失性的并且有机会再次执行(无论是因为内存还是未加载的视图),我会从 dealloc 中删除 [imgView release]; 并在 addSubView 之后离开它。

          以下是基本保留/释放概念的链接: http://www.otierney.net/objective-c.html#retain

          【讨论】:

          • 那么我想知道为什么你会有一个实现在.h文件中表示imgView?我很认真地问因为我不明白。如果您在完成分配 .image 属性后立即释放,为什么不在使用之前实例化 imgView 呢?
          • 你是对的。对于OP,可能没有必要。图像是否在其他地方使用?那么它可能是必要的。
          【解决方案6】:

          是的,该代码有问题。它过早释放 imgView,这可能会在极少数情况下导致崩溃将对象存储在实例变量中而不保留它,而且它通常只是以错误的方式进行内存管理。

          一个正确的方法是:

          @interface SomeViewController : UIViewController
          {
              UIImageView *imgView;
          }
          @property (nonatomic, retain) UIImageView *imgView;
          

          并在实现中;

          @synthesize imgView;
          

          模块中的某处:

          //Create a new image view object and store it in a local variable (retain count 1)
          UIImageView *newImgView = [[UIImageView alloc] initWithFrame:self.view.bounds];
          newImgView.image = [UIImage imageNamed:@"someimage.png"];
          
          //Use our property to store our new image view as an instance variable,
          //if an old value of imgView exists, it will be released by generated method,
          //and our newImgView gets retained (retain count 2)
          self.imgView = newImgView;
          
          //Release local variable, since the new UIImageView is safely stored in the
          //imgView instance variable. (retain count 1)
          [newImgView release];
          
          //Add the new imgView to main view, it's retain count will be incremented,
          //and the UIImageView will remain in memory until it is released by both the
          //main view and this controller. (retain count 2)
          [self.view addSubview:self.imgView];
          

          dealloc 保持不变:

          - (void) dealloc
          {
              [imgView release];
              [super dealloc];
          }
          

          【讨论】:

          • imgView 没有发布得太早。它被 addSubview 保留。这是一个非常常见的习惯用法,在通过 addSubview 添加后立即释放(或任何其他保留的调用,例如 UINavigationController 的 pushViewController。
          • 一个稍微简单一点的方法是直接赋值ivar(imgView),而不是后面使用self.imgView。这消除了代码后面对 [newImgView release] 的需要。
          • @boon 哎呀,你说得很对——我想我一定是误读了原始代码。无论如何,在释放后将某些内容存储在实例变量中是错误的,您最终可能会编写代码在对象被释放后向该对象发送消息(尽管我承认在这种特定情况下这不太可能)。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-05-14
          • 2016-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多