NSNumber 可能会导致内存泄漏。

5
创建一个ARC新项目,并将此代码注入didFinishLaunchingWithOptions函数中。
for (int i=0; i < 1000000; i++) {
    NSNumber* num = [NSNumber numberWithInt:i];
    NSLog(@"%@", num);
}

NSLog(@"over");

应用程序的内存将增加。 此外,在for循环结束时,内存不会减少。
然而,如果在同一个for循环中替换:
NSNumber* num = [NSNumber numberWithInt:i];

by

NSNumber* num = [[NSNumber alloc] initWithInt:i];

那么内存将保持稳定。

这是预期行为吗?

编辑:

好的,那么暂时将didFinishLaunchingWithOptions放在一边。 您仍然可以将其放在viewDidLoad中,并删除一个零以更快地到达for循环的结尾 应用程序的内存将增长到约11 MB。当for循环结束时,内存不会减少。它仍然保持在约11 MB。

- (void)viewDidLoad {
    [super viewDidLoad];

    for (int i=0; i < 100000; i++) {
        NSNumber* num = [NSNumber numberWithInt:i];
        NSLog(@"%@", num);
    }

    NSLog(@"over");
}

如果您选择:

- (void)viewDidLoad {
    [super viewDidLoad];

    @autoreleasepool {
        for (int i=0; i < 100000; i++) {
            NSNumber* num = [NSNumber numberWithInt:i];
            NSLog(@"%@", num);
        }
    }

    NSLog(@"over");
}

惊喜!惊喜! 您将具有完全相同的行为。您将达到约11 MB,内存不会减少。然而,在循环内部放置@autorelease将起作用...但是,在这里,我不是要让事情工作,而是要理解为什么内存在for循环结束时没有被释放。

因此,问题仍然存在。


有趣的.. 等待专家对此发表意见.. - rdurand
如果你在 for 语句周围加上 @autoreleasepool { ... },会发生什么? - trojanfoe
你尝试过其他基础类吗?另一种类型的初始化方法呢(例如采用initWithBool:)? - rdurand
1
你如何测量实际使用的内存量? - hamstergene
你还没有解释你是如何测量你的应用程序的内存使用情况。 - trojanfoe
这只发生在NSNumber类中吗?你尝试过其他类吗? - Nicolas Miari
1个回答

3

这是可以预见的。当您使用alloc/init分配NSNumber时,ARC会在不再需要时立即释放它。

当您使用像numberWithInt:这样的便捷方法进行分配时,您将获得一个在自动释放池中分配的对象。在自动释放池被排空之前,内存不会被释放。但是,您可以为循环创建一个自动释放池,就像这样:

for (int i=0; i < 1000000; i++) @autoreleasepool {
    NSNumber* num = [NSNumber numberWithInt:i];
    NSLog(@"%@", num);
}

NSLog(@"over");

这将在每个查找迭代后释放数字。
你可以在这里获取更多详细信息: 内存管理策略

1
这不可能是原因。主线程已经拥有一个自动释放池,在每次runloop hop时都会释放它。那些数字将在application:didFinishLaunchingWithOptions:退出后的一刻被释放。 - hamstergene
@trojanfoe 是的,会的。 - Jorge
确实。不过语法有点奇怪。 - trojanfoe
1
@hamstergene,它们在方法退出时并没有被释放,我已经尝试过了。这是因为主线程循环在application:didFinishLaunchingWithOptions:方法返回之前不活动。请参见http://goo.gl/Nz9hve。 - Jorge
这是不寻常的,如果是真的,我本来希望在UIApplicationDelegate参考文档中记录并作为Xcode应用模板的一部分提供缺少(系统提供的)autorelease池。仅仅因为事件循环直到稍后才开始,并不意味着autorelease池(在main.m中)不是活动的。 - trojanfoe
@Jorge,这就是我所说的,在application:didFinishLaunchingWithOptions:返回后,运行循环将继续,并隐式提供自动释放池将被清空,导致所有自动释放的对象都会被释放。这是自动释放的主要用例。 - hamstergene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接