【问题标题】:Objects seem to be released but memory footprint keeps going up对象似乎被释放但内存占用不断增加
【发布时间】:2013-10-05 01:09:52
【问题描述】:

我已经开发一个应用程序大约一个星期了,我认为现在是分析它以确保我做的一切正确的好时机,我发现即使我所有的对象都在解除分配的分配量正在增加。当我释放一个对象时,我会执行以下操作:

[object release];
object = nil;

在我的应用程序中,我有一个初始视图控制器,它决定显示我的LoginViewControllerTimeLineViewController,具体取决于我是否有访问令牌。 (这部分无关紧要,因为我遇到的问题在LoginViewController/SignupViewController.)。登录控制器有两个文本字段和两个按钮,这些按钮要么将 sVC 推送到导航视图控制器上,要么尝试登录。

奇怪的是,dealloc 方法正在我的视图和视图控制器上被调用,但在调用它们之后内存却增加了。

SDK 版本 7.0 Xcode 5.0版

编辑:

在我的 LoginViewController 中,当我从 LoginView 中收到 SignupButton 已被单击的事件时,将调用此方法:

- (void)signupButtonPressed
{
    SignupViewController *signupVC = [[SignupViewController alloc] init];
    [self.navigationController pushViewController:signupVC animated:true];
    destroy(signupVC);
}

***注意,destroy宏如下:

#define destroy($x)                                             \
if($x)                                                          \
{                                                               \
[$x release];                                               \
$x = nil;                                                   \
}

SignupViewController创建时ViewDidLoad方法如下:

self.view = [[SignupView alloc] initWithFrame:self.view.frame];

    [[(SignupView *)self.view evtSignupButtonPressed] addHandler:AFFHandler(@selector(signupPressed))];
    [((SignupView *)self.view).profileImage addTarget:self action:@selector(profileImagePressed) forControlEvents:UIControlEventTouchUpInside];

[self.navigationController setNavigationBarHidden:false animated:true];

然后它为 SignupView 中的视图创建 UI,如下所示:

- (void)setupUI
{

    self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:IS_IPHONE5 ? @"genericBackground-568h.jpg" : @"genericBackground.jpg"]];

    _overlayView = [[UIView alloc] initWithFrame:self.frame];

    _scrollView = [[UIScrollView alloc] initWithFrame:self.frame];


    _profileImage = [[UIButton alloc] init];

    profileImageContainer = [[UIView alloc] initWithFrame:CGRectMake(18.5, 0, _profileImage.imageView.image.size.width + 10, _profileImage.imageView.image.size.height + 10)];


    selectProfilePictureText = [[UILabel alloc] initWithFrame:CGRectMake(profileImageContainer.affX, 0, 229, 17)];


    UIView *padding = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 17, 40)];

    _usernameField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 284, 40)];
    _usernameField.delegate = self;

    _passwordField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 284, 40)];
    _passwordField.delegate = self;

    _repeatPasswordField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 284, 40)];

    _repeatPasswordField.delegate = self;

    _emailField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 284, 40)];
    _emailField.delegate = self;

    destroy(padding);

    buttonImage = [[UIImage imageNamed:@"largeButton.png"] copy];
    _submitButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height)];
    [_submitButton addTarget:self action:@selector(signupButtonPressed) forControlEvents:UIControlEventTouchUpInside];


    destroy(buttonImage);




    [self addSubview:_scrollView];
    [self addSubview:_overlayView];
    [_scrollView addSubview:profileImageContainer];
    [profileImageContainer addSubview:_profileImage];
    [_scrollView addSubview:selectProfilePictureText];

    [_scrollView addSubview:_usernameField];
    [_scrollView addSubview:_passwordField];
    [_scrollView addSubview:_repeatPasswordField];
    [_scrollView addSubview:_emailField];
    [_scrollView addSubview:_submitButton];


    destroy(profileImageContainer);
    destroy(selectProfilePictureText);

}

**注意,我省略了所有更改这些对象属性的代码,例如更改背景颜色等。

SignupVC和SignupView的dealloc方法如下:

注册视图:

- (void)dealloc
{

    self.usernameField.delegate = nil;
    self.passwordField.delegate = nil;
    self.repeatPasswordField.delegate = nil;
    self.emailField.delegate = nil;

    AFFRemoveAllEvents();

    destroyAndRemove(_usernameField);
    destroyAndRemove(_passwordField);
    destroyAndRemove(_repeatPasswordField);
    destroyAndRemove(_emailField);
    destroyAndRemove(_profileImage);
    destroyAndRemove(_submitButton);
    destroyAndRemove(_scrollView);
    destroyAndRemove(_overlayView);

    if(buttonImage)
        destroy(buttonImage);


    [super dealloc];
}

SignupVC(在 NavigationBar 的后退按钮被按下后调用)

    - (void)dealloc
    {
        [[(SignupView *)self.view evtSignupButtonPressed] removeHandlersForObserver:self];
        [((SignupView *)self.view).profileImage removeTarget:self action:@selector(profileImagePressed) forControlEvents:UIControlEventTouchUpInside];
        destroy(profileImage);
        destroyAndRemove(self.view);


        [super dealloc];
    }

DestroyAndRemove 这样做:

#define destroyAndRemove($x)                                    \
if($x)                                                          \
{                                                               \
[$x removeFromSuperview];                                   \
[$x release];                                               \
$x = nil;                                                   \
}

【问题讨论】:

  • 您在哪种乐器中看到了这个?
  • 您选择在不使用ARC 的情况下开发现代iOS 应用程序有什么原因吗?
  • 那个destroy宏有点奇怪。
  • 祈祷告诉destroyAndRemove做什么?以及为什么需要如此复杂的发布方案——只需执行[ptrName release] 就足够了,尽管你可以根据需要将其设置为 nil——不需要if 测试。
  • @HotLicks,我同意-if 测试简直是浪费。 @ApperleyA,请考虑:1) 对象已被 released 设置为 nilif 语句将 crash 应用程序或 2) 对象已设置为 @987654343 @,在nil 上调用任何方法都不会做任何事情。 -> if 检查对象是否已释放的语句是不好的做法。

标签: ios objective-c cocoa-touch memory-management ios7


【解决方案1】:

切换到ARC。说真的,没有充分的理由不使用它,它甚至可能解决您的内存问题:

1) Apple 强烈鼓励并表示他们在更新和新应用中使用ARC

2) 绝大多数新应用提交和更新都使用ARC,并且在绝大多数情况下它已被证明与手动引用计数一样有效(您的应用可能也不例外)。

3) ARC 简化了作为 Objective-C 开发人员的生活。您不必再到处乱扔releaseretain 等代码。

4) 有一个易于使用的转换工具:

转到Edit > Refactor > Convert to Objective C ARC

5) 即使您正在使用尚未切换到 ARC 的第三方库(大多数流行的库已经有),您也可以选择不对单个文件使用 ARC。请参阅其他 SO post 了解如何操作。

如果您在切换到 ARC 后仍然遇到问题 - 如前所述,切换到 ARC 可能会解决您的内存问题 - 回来吧,我们会再试一次。

【讨论】:

  • 不是问题的正确答案,但绝对是一个明智的建议。 +1
  • 如果他切换到ARC,它解决了他的问题,我称之为问题的答案。 :D
  • 这有点像在黑暗中拍摄,但是是的^^
  • 我切换到了 ARC(通过向导,它让我进行了几行清理,它为我转换了其余的)。我运行了构建,它像上次一样工作,太棒了。然后我通过仪器运行它,我注意到在启用 ARC 之前与上次相同的行为。如果一个对象有对它的引用,则不会在该对象上调用 dealloc 方法,但它正在被调用,我可以看到它对它的引用为零,因为它不在分配的工具列表中,当它是 Live 的数量为 1 和瞬态是 2(我之前删除了两次视图控制器)
  • 感谢您抽出宝贵时间尝试解决此问题,非常感谢。我接受了这个答案,因为它进行了很好的讨论,并且您在 cmets 中提出了一些要点。祝你好运。
【解决方案2】:

什么物体在吸积?

即使用分配工具时,打开“仅跟踪活动对象”(或等效命名的功能)。然后使用您的应用程序并查看内存中不应该存在的对象。也打开参考事件跟踪器。

这很可能是一个保留周期。也许是一个强引用您的对象的计时器?或者其他类似的关系。

一旦您确定了内存中的对象,您应该点击进入保留和释放的清单,看看哪些是不平衡的。

请注意,您确实应该使用 ARC。

【讨论】:

  • 我已经这样做了,但是没有一个 Views 或 ViewControllers 留在内存中。唯一可能导致这种情况的是我的自定义警报框的委托,但它被设置为弱(为了测试目的,我改用 ARC)并且之前被分配并在解除警报框时设置为 nil。
  • 那么,哪些分配仍然存在?按“现场记忆”排序。某处必须有一些分配。或者,它可能是与图像等相关的映射内存。
猜你喜欢
  • 2021-05-31
  • 2019-11-11
  • 1970-01-01
  • 1970-01-01
  • 2012-07-02
  • 1970-01-01
  • 2011-01-21
  • 1970-01-01
  • 2018-08-27
相关资源
最近更新 更多