在非ARC项目中保留静态数组是否会造成内存泄漏?

5
我有一个以下的测试代码。(通过这个例子,我发现接口不能在没有保留的情况下静态分配。)
通过这段代码,我理解了什么是真正的保留(retain)。我想确保这是否会造成泄漏,我应该在其他地方释放它。简单地说,我不想每次都重新初始化数组,并使其静态化。(虽然会占用更多内存,但速度更快。)
我应该在哪里释放这个被保留的静态数组?这是安全的代码吗?还是我完全删除static和retain这些关键字,只是经典地使用arrayObjects方法进行初始化?那么你对我来说更喜欢什么?
-(NSUInteger)getCoordYByX:(int)ax
{
    NSUInteger ret_=-1;

    static NSArray *coordsX=nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        coordsX=[[NSArray arrayWithObjects:
                      [NSNumber numberWithInt:50],
                      [NSNumber numberWithInt:170],
                      [NSNumber numberWithInt:190],
                      [NSNumber numberWithInt:210],
                      [NSNumber numberWithInt:350],
                      nil]retain];
                      /*it is more longer. cropped for test purposes*/

});
    ret_=[[coordsX objectAtIndex:ax] unsignedIntegerValue];
return  ret_;

总结:
为什么静态数组不能保留初始值?
如果我想使用retain,可能会发生内存泄漏吗?


1
既然它是静态的,那么它应该在整个程序运行期间都存在。那么为什么要在其他地方释放它呢?它就像一个全局变量,只不过它只在函数中可见。 - Ramy Al Zuhouri
在函数内部持有指向静态数据的指针的一个缺点是,你的函数将不可重入。这意味着它不能被多个线程同时安全地调用。 - Daniel Martín
1
@DanielMartín 在这种情况下不行,因为数组是不可变的。 - Ramy Al Zuhouri
抱歉之前声明为类方法的问题已经更正。 我的问题都是关于类实例方法的。 因此,数组值应该在类实例存在时保持不变。我的问题是:当类实例被释放/释放/部署/销毁等是否会造成泄漏。 - Zen Of Kursat
2个回答

1

静态变量只会在应用程序退出前一直存在。静态声明不会导致内存泄漏。我认为你的代码可能存在内存泄漏的风险。因为,

[[NSArray arrayWithObjects:
                  [NSNumber numberWithInt:50],
                  [NSNumber numberWithInt:170],
                  [NSNumber numberWithInt:190],
                  [NSNumber numberWithInt:210],
                  [NSNumber numberWithInt:350],
                  nil]retain];

等于

[[[[NSArray alloc] initWithObjects:
 [NSNumber numberWithInt:50],
 [NSNumber numberWithInt:170],
 [NSNumber numberWithInt:190],
 [NSNumber numberWithInt:210],
 [NSNumber numberWithInt:350],
   nil] autorelease] retain];

所以你的保留消息会造成内存泄漏。

由于数组是静态的,但它是不可变的,也是 autorelease 的,因为使用了方法 arrayWithObjects。如果我移除 retain,static 关键字将无法保持其值,因为存在相同的问题 <autorelease>。 - Zen Of Kursat
我曾认为,如果静态变量被分配后,它也会保留数组的值。但实际上并不是这样的。 - Zen Of Kursat
你确定在类实例方法块中声明的静态变量会保留其值吗? - Zen Of Kursat

1

这只是一种声明全局、不可变对象数组的方法之一。

从内存泄漏的角度来看,这并不算是一种泄漏,因为没有分配的内存是无法访问的。更进一步地说(也就是说有些荒谬),即使你的程序使用-retain保留了所有这些对象,根据无法访问内存的定义,仍然不会出现内存泄漏。该数组只被初始化一次且从未重新赋值,因此,该初始数组将始终对您的方法可用,并且其元素始终可以通过数组访问。

然而,这是一种无法回收内存的方式,直到程序终止(假设进入方法体)。因此,我不鼓励这种设计(事实上,我避免使用这种设计)。

更好的替代方案看起来更像:

+ (NSUInteger)coordYByX:(int)ax
{
 enum { NCoordsX = 5 };
 const NSUInteger CoordsX[NCoordsX] = { 50,170,190,210,350 };
 assert(ax < NCoordsX && "invalid index"); // handle error as you handle errors
 return CoordsX[ax];
}

这需要零堆分配,零静态存储和零锁定;)

好的答案。更好的方法。 但是: 我正在寻找答案。 只是想弄清楚为什么静态变量不保留初始值。 如果我想使用retain,会发生内存泄漏吗? - Zen Of Kursat
@N.Ramos 无论它是在类方法还是实例方法中声明的,都不会改变我的答案 - 对两者来说都是一样的 :) - justin
我正在借用你的答案,这更方便。但是我正在尝试理解为什么静态变量不保留数组的初始值。如果我想使用retain会发生内存泄漏吗? - Zen Of Kursat
@N.Ramos 如果你想要一个对象数组,你发布的程序已经是正确的。它将以线程安全的方式创建一个NSNumber数组,并且对象具有适当的引用计数。该数组保存对其元素的引用,并且写入的-retainstatic变量coordsX的保留引用(这是必要的)。该数组及其元素将一直存在,直到你的程序退出。我不明白为什么你在这个例子中看到了不同的行为。 - justin

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