【问题标题】:Abandoned memory issue废弃内存问题
【发布时间】:2011-03-17 16:44:55
【问题描述】:

您能帮帮我吗,我的应用程序快完成了,我现在正在优化它,所以我正在使用 Instrument Allocations 工具,我有一个视图控制器的问题,但我不知道找出为什么每次我从主视图转到详细视图控制器时内存都会增加。

这似乎是问题的代码部分:

  - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

// Create and push a detail view controller.

    self.entriesDetailedViewController = [[EntriesDetailedViewController alloc]init];

Entry *selectedEntry = (Entry *)[[self fetchedResultsController] objectAtIndexPath:indexPath];

self.entriesDetailedViewController.entry = selectedEntry;

[self.navigationController pushViewController:self.entriesDetailedViewController animated:YES];
[self.entriesDetailedViewController release];
}

这里是detailedViewController.h的代码:

  #import <UIKit/UIKit.h>


  @class Entry;

 @interface EntriesDetailedViewController : UIViewController <UITextViewDelegate> {

//IBOutlet UIButton *createEntryButton;
IBOutlet UITextField *entryTextField1;
IBOutlet UITextView *entryTextField2;
IBOutlet UIBarButtonItem *textbodyBarButton;
IBOutlet UIBarButtonItem * catLabel;
IBOutlet UINavigationBar *entryNameToolBar;
IBOutlet UINavigationBar *textBodyToolBar;
IBOutlet UIImageView *reviewCheck;
IBOutlet UIImageView *textBackground;
IBOutlet UIBarButtonItem *reviewButton;
BOOL isChecked;

NSManagedObjectContext *managedObjectContext;


Entry *entry;

    }


 @property (nonatomic,retain) IBOutlet UITextField *entryTextField1;
 @property (nonatomic,retain) IBOutlet UITextView *entryTextField2;
 @property (nonatomic,retain) IBOutlet UIBarButtonItem *textbodyBarButton;
 @property (nonatomic,retain) IBOutlet UIBarButtonItem *catLabel;
 @property (nonatomic,retain) IBOutlet UINavigationBar *entryNameToolBar;
 @property (nonatomic,retain) IBOutlet UINavigationBar *textBodyToolBar;
 @property (nonatomic,retain) IBOutlet UIImageView *reviewCheck;
 @property (nonatomic,retain) IBOutlet UIBarButtonItem *reviewButton;
 @property BOOL isChecked;
 @property (nonatomic,retain) IBOutlet UIImageView *textBackground;

 @property (nonatomic,retain) NSManagedObjectContext *managedObjectContext;
 @property (nonatomic, retain) Entry *entry;


 - (void)setUpUndoManager;
 - (void)cleanUpUndoManager;
 - (void)textViewDidBeginEditing:(UITextView *)entryTextField2;
 - (void)textViewDidEndEditing:(UITextView  *)entryTextField2;
 - (void)saveContext;



 - (IBAction)dismisskeyboard;
 - (IBAction)dismissKeyboardfromTextView;
 - (IBAction) selectReview;


 @end

这是detailedViewController.m:

  #import "EntriesDetailedViewController.h"
  #import "Entry.h"
  #import "TheLearningMachineAppDelegate.h"


  @implementation EntriesDetailedViewController

  @synthesize entryTextField1,entryTextField2,textbodyBarButton,managedObjectContext,catLabel, entryNameToolBar,textBodyToolBar, reviewCheck,reviewButton,isChecked,textBackground, entry, undoManager;


  #pragma mark -
  #pragma mark View lifecycle


  - (void)viewWillAppear:(BOOL)animated {

  }

  - (void)viewDidLoad {
      [super viewDidLoad];

  // Configure the title, title bar, and table view.

      self.title = @"Consultation";

    UIBarButtonItem *rightButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(save:)]; 
self.navigationItem.rightBarButtonItem = rightButton; 
[rightButton release];

[self setUpUndoManager];

self.entryTextField1.text = self.entry.entryname;
self.entryTextField2.font = [UIFont fontWithName:@"Helvetica" size:17.0];
self.entryTextField2.text = self.entry.textbody;
self.catLabel.title = self.entry.category;
[self.entryTextField2 setFrame:CGRectMake(24, 56, 273, 140)];

self.textBackground.image = [UIImage imageNamed:@"alert_background.png"];
[self.textBackground setFrame:CGRectMake(0,46,312,140)];



  //Initializing a kind of checkBox button

if ([self.entry.active boolValue] == YES) {

    self.reviewCheck.image = [UIImage imageNamed:@"checkedwhitesquare.png"];

    self.isChecked = YES;
}

else {

    self.reviewCheck.image = [UIImage imageNamed:@"UncheckedWhiteSquare.png"];

    self.isChecked = NO;

}

  }


  - (IBAction) selectReview {

      if (isChecked==NO){
        self.reviewCheck.image = [UIImage imageNamed:@"checkedwhitesquare.png"];
    self.isChecked = YES;
    [self.entry setValue:[NSNumber numberWithBool:YES] forKey:@"active"];
    [self performSelector: @selector(save:)];

}
else {

    self.reviewCheck.image = [UIImage imageNamed:@"UncheckedWhiteSquare.png"];

    self.isChecked = NO;
    [self.entry setValue:[NSNumber numberWithBool:YES] forKey:@"active"];
    [self performSelector: @selector(save:)];

}

  }



  - (void)textViewDidBeginEditing:(UITextView  *)entryTextField2{
[self.navigationController setNavigationBarHidden:YES animated:YES];
self.entryNameToolBar.hidden =YES; 
self.textBodyToolBar.hidden =NO;
self.textbodyBarButton.style =UIBarButtonItemStyleBordered;
self.textbodyBarButton.title =@"Terminer la saisie";
[self.entryTextField2 setFrame:CGRectMake(24, 50, 273, 140)];
[self.textBackground setFrame:CGRectMake(0,46,312,140)];
  }

  - (IBAction)textViewDidEndEditing: (UITextView  *)entryTextField2{
      [self.navigationController setNavigationBarHidden:NO animated:YES];
      self.entryNameToolBar.hidden =NO;
      self.textBodyToolBar.hidden =YES;
      [self.entryTextField2 setFrame:CGRectMake(24, 50, 273, 140)];
      [self.textBackground setFrame:CGRectMake(0,46,312,140)];
      self.textbodyBarButton.style =UIBarButtonItemStyleDone;
      self.textbodyBarButton.title =@"Corps de texte";  

  }

  - (IBAction)dismisskeyboard{

[self.entryTextField1 resignFirstResponder];
  }

  - (IBAction)dismissKeyboardfromTextView{

[self.entryTextField2 resignFirstResponder];

  }

  - (void)saveContext {

      NSError *error = nil;
      if (self.managedObjectContext != nil) {
          if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error]) {

        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Erreur" 
        message:@"blabla" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
        [alert release];


        abort();

          } 
      }
  }  


  - (IBAction)save:(id)sender {

      // if there's text in textfield1 and textfield2 and a category has been set 
      if (([self.entryTextField1.text length]!= 0) && ([self.entryTextField2.text length]!= 0) && ([self.catLabel.title length]!= 0)){ 

    [self.entry setValue:self.entryTextField1.text forKey:@"entryname"];
    [self.entry setValue:self.entryTextField2.text forKey:@"textbody"];
    [self.entry setValue:self.catLabel.title forKey:@"category"];

    if (self.isChecked == YES) {
        [self.entry setValue:[NSNumber numberWithBool:YES]forKey:@"active"];
    }
    else {

        [self.entry setValue:[NSNumber numberWithBool:NO] forKey:@"active"];
    }

    [self.entry setValue:[NSNumber numberWithBool:NO] forKey:@"editable"];

    //save
    NSError *error;
    [self.managedObjectContext save:&error];
    [self saveContext];

    //Return to mainview
    [self.navigationController popViewControllerAnimated:YES];
    [self.entry release];
    //self.entry =nil;
}

else {

    if([self.entryTextField1.text length] ==0){

        UIAlertView *alert =[[UIAlertView alloc] 
                             initWithTitle:@"Oooops..." 
                             message:@"Your entry has no name" 
                             delegate:nil 
                             cancelButtonTitle: @"ok" 
                             otherButtonTitles:nil];
        [alert show];
        [alert release];

    }
    else {

        if ([self.entryTextField2.text length]==0){

            UIAlertView *alert =[[UIAlertView alloc] initWithTitle:@"Oooops..." message:@"Your list is empty" delegate:nil cancelButtonTitle: @"ok" otherButtonTitles:nil];
            [alert show];
            [alert release];

        }
        else {

            if ([self.catLabel.title length]==0){

                UIAlertView *alert =[[UIAlertView alloc] initWithTitle:@"Oooops..." message:@"You need to choose a category for this list first." delegate:nil cancelButtonTitle: @"ok" otherButtonTitles:nil];
                [alert show];
                [alert release];

                  }
              }

          }
      }
   }


  - (void)setEditing {
  //    [super setEditing:editing animated:animated];
  }




  #pragma mark -
  #pragma mark Undo support

  - (void)setUpUndoManager {

    if (self.entry.managedObjectContext.undoManager == nil) {

    NSUndoManager *undoMgr = [[NSUndoManager alloc] init];
    [undoMgr setLevelsOfUndo:1];
    self.entry.managedObjectContext.undoManager = undoMgr;
    [undoMgr release];


    }

    // Register as an observer of the entry's context's undo manager.
    NSUndoManager *entryUndoManager = self.entry.managedObjectContext.undoManager;
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
    [dnc addObserver:self selector:@selector(undoManagerDidUndo:) name:NSUndoManagerDidUndoChangeNotification object:entryUndoManager];
    [dnc addObserver:self selector:@selector(undoManagerDidRedo:) name:NSUndoManagerDidRedoChangeNotification object:entryUndoManager];


  }


  - (void)cleanUpUndoManager {

      // Remove self as an observer.
      [[NSNotificationCenter defaultCenter] removeObserver:self];

      if (self.entry.managedObjectContext.undoManager == undoManager) {
          self.entry.managedObjectContext.undoManager = nil;
          self.undoManager = nil;
      }       
  }


  - (NSUndoManager *)undoManager {
      return self.entry.managedObjectContext.undoManager;
  }


  - (void)undoManagerDidUndo:(NSNotification *)notification {

  }


  - (void)undoManagerDidRedo:(NSNotification *)notification {

  }

  - (BOOL)canBecomeFirstResponder {
      return YES;
  }


  - (void)viewDidAppear:(BOOL)animated {
      [super viewDidAppear:animated];
      [self becomeFirstResponder];
  }


  - (void)viewWillDisappear:(BOOL)animated {
      [super viewWillDisappear:animated];
      [self resignFirstResponder];
  }

- (void)viewDidUnload {

// Release any properties that are loaded in viewDidLoad or can be recreated lazily.


self.entryTextField1 =nil;
self.entryTextField2 = nil;
self.catLabel = nil;
self.entryNameToolBar = nil;
self.textBodyToolBar = nil;
self.reviewCheck = nil;
self.textBackground = nil;
self.reviewButton = nil;
self.entry = nil;
self.navigationItem.rightBarButtonItem = nil;
[self cleanUpUndoManager];
self.entry.managedObjectContext.undoManager = nil;


}

  - (void)dealloc {

[entryTextField1 release], entryTextField1 = nil;
[entryTextField2 release], entryTextField2 = nil;
[catLabel release], catLabel = nil;
[entryNameToolBar release], entryNameToolBar = nil;
[textBodyToolBar release], textBodyToolBar = nil;
[reviewCheck release], reviewCheck = nil;
[textBackground release], textBackground = nil;
[reviewButton release], reviewButton = nil;
[self cleanUpUndoManager];
[entry.managedObjectContext.undoManager release],  
     entry.managedObjectContext.undoManager = nil;
[entry release],entry = nil;
[managedObjectContext release];


[super dealloc];
}

对不起,我知道这是一段很长的代码,但我不知所措。请帮帮我。

按照你们的建议,我做了以下修改:

我还更改了代码的 viewDidUnload 和 Dealloc 部分,现在看起来像这样:

我不太了解 release + nil 的概念,也不太了解在 dealloc 中调用 [self.object release] 和 [object release] 与在 viewDidUnload 中调用 self.object = nil 之间的区别。

【问题讨论】:

  • 这里的内容太多了,任何人都无法为您提供帮助。您需要更具体一些,所以我的第一个建议是您确定是否肯定是您的EntriesDetailedViewController 导致内存使用量增加。为此,只需在EntriesDetailedViewController 实例中的dealloc 方法中添加一个断点,然后查看当您从导航堆栈中弹出它时它是否被调用。如果没有,您就知道问题出在该控制器内部的某个地方。然后我们就可以开始帮助您了!
  • 这里有一个对didSelectRowAtIndexPath 方法的小建议:`EntriesDetailedViewController *vc = [[EntriesDetailedViewController alloc] init];条目 *selectedEntry = (Entry *)[[self fetchedResultsController] objectAtIndexPath:indexPath]; vc.entry = selectedEntry; [self.navigationController pushViewController:vc 动画:YES]; [vc 发布];}
  • @Rog - 您好,我实现了您在上面建议的关于 vc 的更改(两个字母肯定更好),是的,断点是在 dealloc 中调用的。请参阅上面的代码中的 dealloc 的样子。我目前正在阅读有关仪器分配如何工作的文档。
  • @Miky 酷,如果dealloc 被调用,您可以消除您的detailsViewController 作为问题的一部分。如果在其范围内有任何保留对象,则不会调用控制器的 dealloc 方法。
  • @Rog - 那我该怎么办?上吊自己? ;-)

标签: ios objective-c memory abandoned-memory


【解决方案1】:

您应该期望每次分配新的EntriesDetailedViewController 时内存使用量都会增加。我假设你的意思是当你释放它时你的记忆不会回落?你可能没有在-[EntriesDetailedViewController dealloc] 中正确发布某些内容。

请注意,与其每次都生成一个新的EntriesDetailedViewController,不如在第一次需要它时创建一个,然后继续使用setEntry: 重新配置它。您已经设置了 ivar 来保存视图控制器;不妨利用它。

【讨论】:

  • 嗨,我希望我知道该怎么做... ;-(。如果这不是要求太多,你能用一些代码来说明它吗?
【解决方案2】:

原因在您发布的代码中并不明显,但 Instruments 会向您显示每次分配发生的位置,因此应该不难追踪。

【讨论】:

  • 您的-viewDidLoad 分配了一个条形按钮项而不释放它:self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(save:)];
  • 你是对的——我错过了。但是,我建议您在将其分配给导航项后将其释放。如果该项目想要保留它,它将保留它。如您所知,导航项可能正在复制它,而您可能不恰当地发布了它的副本。我怀疑这真的会发生,但通常你不想释放其他对象的属性。
  • @Caleb - rightBarButton 在 dealloc 中。是不是正常发布了?
  • 感谢您的提示。然而,它几乎没有影响,我的 heapshots 显示 heap 每次增长 20 ko。
  • 不要强调这一点,而是这样做:UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] init...]; self.navigationItem.rightButtonItem = rightButton; [rightButton release]; 否则,您将依赖于假设导航项返回您设置的相同对象。碰巧确实如此,但如果 rightButtonItem 属性被指定为“复制”而不是“保留”,则不会,此时您将释放错误的对象。这既是泄漏,也是错误的发布。
【解决方案3】:

你应该在 dealloc 中使用这个表单:

[entryTextField1 release], entryTextField1 = nil;

你应该使用这个表格来设置(例如viewDidUnload):

self.entryTextField1 = nil;

运行静态分析应该会发现更多问题。修复它们。

一旦所有这些都得到纠正,然后重新运行该应用程序。 (我假设这不会是 100% 的覆盖率)

另外,请确保您使用版本控制。您会发现自己正在寻找很多问题 - 在它们全部解决之前,您应该会遇到更多问题/崩溃。

不幸的是,您无法永远避免内存问题,而且要找出其中的一些问题是很痛苦的——同时要学习正确的内存管理(天,很难)。祝你好运!

【讨论】:

  • 好吧,关于 entryTextField1 和其他内容,我按照您所说的做了。我将编辑上面的代码以显示更改。我尝试了静态分析工具,但它似乎没有在详细视图控制器上报告任何 pb。
  • 我提出的第二点(关于 setter)没有实现/更新 - 您必须使用 'self' 通过访问器发送消息。它是负责管理 ivar 对对象的引用计数的访问器。您的撤消管理器也在 dealloc 中过度释放(当未返回 nil 时)-您真的需要 ivar 吗?
  • 好的,我将立即为 viewDidUnload 编辑代码,并为 undomanager 更改代码。现在正确吗?哦,顺便问一下,你知道我在哪里可以找到一个很好的教程吗?
  • 你好,只是一个简单的问题,实际上,我有一个 rootView 控制器,它推送包含 tableView 的 entriesViewConroller。当您单击一行时,您将转到entryDetailedViewController。当您问我是否调用了 dealloc 时,我说是,但实际上,它仅在您返回 rootViewController 时调用,而不是在您转到 entriesDetailedViewController 时调用。这正常吗?
【解决方案4】:

嘿嘿,你们知道吗?我才发现!!!!!!!! 我是多么的愚蠢…… 如果你仔细查看 entryDetailedViewController.h 文件,你会发现有一个 textbodyBarButton 被声明了......好吧,它没有被释放,我忘记了那个......

所以我很抱歉让你浪费了你的时间。

我刚刚添加了:

    self.textbodyBarButton = nil;
[textbodyBarButton release];

我现在很好,当我回到rootViewController时,所有的内存都被释放了。

所以如果你们有一天也遇到同样的问题,那可能就是这么愚蠢。只需检查两次您在 .h 文件中声明的内容。

哇哦,不过花了 3 天时间...

感谢大家,再次感谢您的耐心等待。

【讨论】:

    猜你喜欢
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 2012-02-29
    • 2011-06-10
    • 2012-06-17
    • 2019-03-21
    • 2011-04-23
    • 1970-01-01
    相关资源
    最近更新 更多