声明,分配,加载和释放NSMutableArray的正确方法

时间:2010-02-22 00:07:04

标签: objective-c iphone nsmutablearray dealloc

我在我的* .h文件中声明我的数组:

@interface aViewController: UIViewController
{
   NSMutableArray *anArray;    // You will need to later change this many times.
}
@end

我为我的* .m文件分配内存:

-(void) viewDidLoad
{
   anArray = [[NSMutableArray alloc] init];
}

我单击一个测试按钮来加载我的数组(最终它需要加载DIFFERNT值 每次点击):

   anArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil];

我在这里释放它:

-(void) dealloc
{
   [anArray release];
   [super dealloc];
}

这一切看起来都好吗?

因为我以后运行此代码时崩溃了:

   NSLog(@"%d", [anArray count]);

不确定为什么简单的“NSLog()和count”会使一切崩溃。


德克,

让我这样说吧:我对指针,数组,字符串和内存有一个巨大的误解。

我已经阅读了我能在其上找到的所有内容...但是(尚)找到一个简单,清晰,易于理解的描述。

你可以推荐一个吗? (希望阅读不到10页。) 是否有一个参考解释 JUST 这个主题...并且从“你有12年的编码经验......但没有处理过分配内存或指针”的观点来看。)

所以变量名是 NOT 我引用对象的方式? 那为什么呢?

我已经习惯了许多其他语言:

myString = "this"
myString = "that"

myInt = 5
myInt = 15

(可能更简单。)


在我看来,这是最简单的方法。 (它似乎有效。但这是真的正确吗?)

Don't alloc any memory for my NSMutableArray initially.
Don't re-alloc any memory repeatedly.  (When I change my array's values.)
Don't release it repeatedly. (Before I change my array's values.)
Don't release it when I exit the program.

But:
Always remember to use RETAIN when I initially assign 
(and repeatedly reassign) new values to my anArray variable.

  

您没有加载数组   anArray = [NSMutableArray arrayWithObjects:@“one”,@“two”,@“three”,nil];   相反,你用新的实例替换它,更糟糕的是:用一个实例替换它   引用实际上由您无法控制的某个实体拥有

哇。所以我可以拥有20个数组......所有的名字都叫同一个名字:anArray ......它们会有所不同吗? (没有GLOBAL数组这样的东西?)

  

等。为了清除旧值,方法removeAllObject可能很方便。   还有一些变异方法,可用于一次添加多个值。

所以......首先我必须“删除所有对象”......然后我可以调用 ONE 方法重新添加所有新值。

  

anArray = [[NSMutableArray arrayWithObjects:@“one”,@“two”,@“three”,nil] retain];   而不是alloc / init序列。

哇。我认为没有任何东西可以存储在数组中而不为它分配空间。

  

如果您真的打算更换整个阵列,您可能需要考虑   使用属性

我如何使用属性呢?
做这样的事情的正确方法是什么:

> anArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil];
> anArray = [NSMutableArray arrayWithObjects:@"four", @"five", @"six", nil];

就像我一样:

x = 12;
x = 24;

哇。我真的完全误解了字符串,数组和内存的一切。 我认为“简单方法”是分配ONCE ...使用可变数组...根据需要更改它...并释放它。

  

这样做的问题是不保留这个新数组,

我认为旧阵列将会消失......并且可以使用新阵列。 (但我猜不是。)

  

此外,您有内存泄漏,因为您从未释放原始数组。

我在想,旧的阵列不应该被释放......我没有完成它...我只是想改变它以包含我的新值。 (但我猜不是。)

  

但是一个是使用[anArray release];

我在想这会让我释放我分配的内存......(但我猜不是)......然后我必须重新分配更多的内存。 (但我猜不是。)

  

anArray = [[NSMutableArray arrayWithObjects:@“one”,@“two”,@“three”,nil] retain];

所以我必须“保留”它...所以它不会从我身下消失? (不知道为什么会这样。直到告诉它......在我最后的dealloc电话中。)

  

另一种可能更正确的修复方法是使用addObject:或   addObjectsFromArray:NSMutableArray方法,而不是不断创建新数组。

我只想创建 ONE 数组......并且只是按照我的意愿使用它。 我从不想 ADD 到数组。我想将它设置为我的新值。

3 个答案:

答案 0 :(得分:11)

您没有使用

加载数组
anArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil];

相反,您要用新实例替换它,更糟糕的是:对于一个实例,其引用实际上由您无法控制的某个实体拥有(最有可能是NSAutoreleasePool。)您正确拥有的引用,由

创建的
[[NSMutableArray alloc] init]

丢失了,将被泄露。

使用例如addObject:之类的

,而不是替换整个数组引用,而是改变您已经拥有的那个引用。
[anArray addObject: @"one"]
[anArray addObject: @"two"]

等。为了清除旧值,方法removeAllObject可能很方便。还有一些变异方法,可用于一次添加多个值。

或者,您可以使用已经使用的构造方法分配数组,但要小心保留它。在viewDidLoad

anArray = [[NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil] retain];

而不是alloc / init序列。

如果您真的想要替换整个阵列,您可能需要考虑使用properties而不是手动进行引用计数。

答案 1 :(得分:4)

问题是anArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil];这会替换您最初创建的数组。这样做的问题是不保留这个新数组,所以一旦方法返回就会丢失它。此外,您有内存泄漏,因为您从未释放原始数组。

有几种方法可以解决此问题,但其中一种方法是使用[anArray release]; anArray = [[NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil] retain];代替。

另一种可能更正确的解决方法是使用addObject:addObjectsFromArray: NSMutableArray方法,而不是不断创建新数组。

答案 2 :(得分:3)

请记住这个简单的内存规则:仅发布您拥有的对象。只有在使用以下方法创建对象时才拥有对象:

  • init (此方法创建一个保留计数为1的新对象)
  • new (此方法与使用alloc和init相同)
  • copy (此方法创建一个保留计数为1且具有方法接收者内容的新对象)
  • retain (此方法会将保留计数增加为1)

系统dealloc自动执行保留计数为零的对象。完成后,您必须release您拥有的每个对象。如果你release太快,你会遇到危险的情况。如果你完成它后没有release你的对象,你就会泄漏。

指针是一个特殊对象,它引用内存中的某个对象。它基本上是一个内存地址(和一些其他数据)。如果要使用对象,则必须alloc为其存储内存,然后init ialize。您(通过使用=符号)指定指针。

string = [[NSString alloc] init];

string现在有一个保留计数。但由于NSString是一个不可变对象,因此初始化后无法更改。分配string值的一种方法是在初始化时进行。

string = [[NSString alloc] initWithString: @"Hello, World!"];

如果您需要定期更改string,则可以使用另一个可变的类:NSMutableString。但是,改变它们的唯一方法就是留言。

string = [[NSMutableString alloc] initWithString: @"Initial string"];
[string setString: @"Modified string"];

请注意,以下代码错误并导致内存泄漏。

string = [[NSMutableString alloc] initWithString: @"Initial string"];
string = @"Modified string";

在第一行中,string被分配给新创建的对象。在第二个中,string被分配给另一个字符串。您丢失了对新创建的对象的引用,并且您发现了泄漏:您无法释放您没有引用的对象。

使用整数(int类型)执行此操作时不会出现问题,因为它是“本机”对象。 Objective-C Runtime直接将这些数据类型与其值相关联,而不是指针。

int1 = 4;
int1 = 5;

注1。不要忘记始终告诉运行时指针的类型。如果要使用string,则必须先在标题中定义它(如果它是公共的)或实现(如果您希望它从其他方法中隐藏)。然后你可以自由使用这个名字。

NSString *string;

星星告诉运行时它是一个指针。对于本机类型,没有指针,所以你得到它。

int int1;

注意2。数组(和字典等)的行为与NSString完全相同,因为它们具有不可变和可变的变体。

注3。大多数类都有一些特殊的方法可以同时分配,初始化和自动释放。

NSArray *myArray = [NSArray arrayWithObjects: @"Butter", @"Milk", @"Honey", nil];

自动释放的对象不需要手动release,因为系统会在一段时间后为您release。在仅在方法持续时间或方法之间需要对象的情况下,它非常方便,而不是作为对象的永久部分。当你在主线程上autorelease某个对象(在带有GUI的app中)时,当事件循环结束当前循环时它将是release d(当处理某个事件时它结束)。在Apple的文档中阅读有关Autorelease Pools的更多信息。

我希望我帮助那些不了解指针的人。在我进行大量实验之前,我自己有几个月的问题。 : - )