移除对象时内存未释放 - 不清楚在 ARC 中释放的正确方法

Posted

技术标签:

【中文标题】移除对象时内存未释放 - 不清楚在 ARC 中释放的正确方法【英文标题】:Memory not releasing when removing object - unclear on proper way to release in ARC 【发布时间】:2014-06-29 14:49:28 【问题描述】:

Xcode 5、ios7

我有一个应用程序在循环中创建 UILabel,并在数组中引用它们。 在反复调用“创建”方法后,我注意到我的内存使用量增加了。 我想清除数组和相关的 UILabel 对象以释放内存, 因为它似乎没有发生在我当前的代码中。相反,每次通过我的循环 我可以看到我的内存使用量在增加并且从未减少。我目前正在使用 Storyboards 和 ARC。

既然 ARC 似乎不适合我,我该如何正确释放 UILabel?

@implementation myViewController
CGPoint tmpPoint;
NSMutableArray *allDots;
/// more code here
//
// This method is called repeatedly from another loop
-(void)createLabels
//remove any previous labels
//I'm pretty sure this is not actually clearing the UILabels but rather the pointer to the labels?
    for(short i=0; i<10; i++)
        UILabel *junkDot=[allDots objectAtIndex:i];
        junkDot=nil;
    
// Either of these statements should clear the references/pointers
//        [allDots removeAllObjects];
    allDots=[[NSMutableArray alloc] init];

//create a new group of labels
    for(short i=0; i<10; i++)
        // code to generate Point values
        [self makeLabel:tmpPoint];
    


-(UILabel*)makeLabel:(CGPoint)thePoint
    CGRect xFrame=CGRectMake(thePoint.x, thePoint.y, 40, 20);
    UILabel *tmpLabel=[[UILabel alloc] initWithFrame:xFrame];

    [self.view addSubview:tmpLabel];

    [allDots addObject:tmpLabel];

    return tmpLabel;

【问题讨论】:

你怎么知道内存没有被释放? (即你如何测量内存使用情况?) 我注意到使用该应用程序一段时间后,移动变得非常缓慢。然后,当我从 Xcode 运行我的应用程序时,我观察了 Activity Monitor>Memory 并且它缓慢地继续攀升,即使它应该是稳定的(除了我添加/删除对象时的轻微上升/下降) 【参考方案1】:

您需要阅读内存管理的基础知识。

在 ARC 中,只要至少有一个对它们的强引用,对象就会保存在内存中。一旦不再有对对象的任何强引用,它就可能被释放。

NSArray(或 NSMutableArray)保持对添加到其中的任何对象的强引用。如果你从一个可变数组中移除一个对象(例如使用removeObjectAtIndex:),那么数组就会释放它的强引用。

从数组中获取对象然后将局部变量设置为 nil 的代码绝对不会浪费时间:

for(short i=0; i<10; i++)
    UILabel *junkDot=[allDots objectAtIndex:i];
    junkDot=nil;

当您使用addSubview: 将视图对象添加到视图层次结构时,超级视图也会保留对该对象的强引用。

因此,如果您创建一堆标签并将它们放在一个数组中并将它们添加为内容视图的子视图,则每个标签都有 2 个强引用。标签在从其父视图中删除并从数组中删除之前不会被释放。

【讨论】:

1) 是的,我确实需要阅读! 2) 这是否也会释放引用:allDots=[[NSMutableArray alloc] init] 而不是循环遍历 removeObjectAtIndex? 3) 如何从超级视图中移除对象? 您问“这是否也会释放引用:allDots=[[NSMutableArray alloc] init] 而不是循环遍历 removeObjectAtIndex?”啊?!?为什么要创建一个新数组来释放另一个数组中的对象?如果您有一个包含视图对象的现有 NSMutableArray allDots,您可以使用 removeAllObjects 一次删除数组中的所有对象。但是,这将我们带到第 3 项:如果您已将视图添加到视图控制器的内容视图,则需要向每个视图发送 removeFromSuperview 消息。 看看你是否能找出一个循环来从 self.view 中删除 allDots 数组中的所有视图,然后在循环完成后从数组中删除所有对象。提示:查看for.. in 循环语法、removeFromSuperview 和 removeAllObjects。 知道了 - 使用了 removeAllObjects 和 removeFromSuperView ,它似乎有帮助。谢谢。 好问题 - 我希望人们在不提供理由的情况下放弃这样做。【参考方案2】:
@implementation myViewController
CGPoint tmpPoint;
NSMutableArray *allDots;

用代码替换上面的代码

@implementation myViewController 
CGPoint tmpPoint;
NSMutableArray *allDots;

【讨论】:

你能解释一下这会有什么帮助吗? 上述声明数组对于应用来说是全局的。它会在应用停止时释放。类实现括号中的数组声明下面是类实例的私有对象。因此,它会在超级对象被释放时释放。对不起,我没有提到我的代码描述。 @wayneh:这里是你的答案***.com/a/22640702/3765200 它说的是 GloablData *globDat; 的声明;它的全局用于应用程序的结束。当应用程序终止时它会自动释放。我说的对吗?【参考方案3】:

除非您将 UILabel 对象保留在视图中的屏幕上,否则它们应该从数组中删除,至少只需使用 removeAllObjects 方法即可。如果他们不是,那么其他东西正在保留他们。

在您的代码中,您正在从 allDots 数组中提取项目,但是您仍在其他地方使用 UILabel。该数组已释放该对象,但由于它仍在某处使用。由于数组只保存引用,因此您只需将保留计数减少 1。对象不会占用更多或更少的内存,它只是少了一个保留。

您可以轻松设置一些 NSLog 或沿途记录断点,以查看发生了什么,如下所示:

- (void)viewDidLoad 
    [super viewDidLoad];

    NSMutableArray *allDots = [@[] mutableCopy];

    for(NSInteger i=0; i<10; i++)
        UILabel *i = [UILabel new];
        [allDots addObject: i];
    
    NSLog(@"Contents of array %@", [allDots description]);
    // Either of these statements should clear the references/pointers

    [allDots removeAllObjects];
    NSLog(@"Contents of array %@", [allDots description]);
    // Do any additional setup after loading the view, typically from a nib.


2014-06-29 11:25:59.185 Loper[47174:2208182] Contents of array (
"<UILabel: 0x10b76c370; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b768ef0>>",
"<UILabel: 0x10b82b870; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b82b9d0>>",
"<UILabel: 0x10b834c10; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b82d510>>",
"<UILabel: 0x10b82a9b0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b8408b0>>",
"<UILabel: 0x10b82ab10; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b834dd0>>",
"<UILabel: 0x10b8326e0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b837a80>>",
"<UILabel: 0x10b832840; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b80ebb0>>",
"<UILabel: 0x10b8329a0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b841a90>>",
"<UILabel: 0x10b846290; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b80eeb0>>",
"<UILabel: 0x10b8465a0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x10b846700>>"
)

2014-06-29 11:25:59.185 Loper[47174:2208182] Contents of array (
)

要逐步了解发生了什么,只需在其中设置一个常规断点并单步执行代码以查看谁在保留引用。

其他选项是仅运行静态分析器并查看是否检测到任何保留循环,或者使用泄漏或分配工具在 Instruments 中打开您的项目,然后查看设备上的幕后情况。

【讨论】:

以上是关于移除对象时内存未释放 - 不清楚在 ARC 中释放的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

为啥 ARC 在 popViewController 之后不释放内存

将 uiimage 设置为 nil 不会使用 ARC 释放内存

iOS:ARC,不释放内存

oc56--ARC多个对象的内存管理

对象被释放了,它们仍然给出了它们的值。 ARC 未标记(表示关闭)

iOS 内存管理