2009-02-09 103 views
12

我是新来的编码,并试图加快Objective-C的速度。 遇到一些我不明白的代码。我希望有人可以为我澄清它。在下面的情况下,我不确定* foo2是如何工作的以及为什么它没有被释放?Objective-C指针?

ClassOne *pointer = [[ClassOne alloc]init]; 

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

foo2 = [foo add: pointer]; 
[foo release]; 
foo = foo2 

[pointer release]; 

[foo release]; 
+0

这些类的类名是什么? – 2009-02-09 19:26:37

+2

顺便说一下,我不确定这是否是在匿名代码或什么的过程中产生的,但是由于代码只有七行,所以代码很混乱。 – Chuck 2009-02-09 19:40:55

回答

1

因为您还没有发布它。你在这里发布引用,而不是释放指针。这不完全是一回事。当您第二次执行[foo发布]时,您将释放您在将foo2指定为foo时创建的foo引用。

要释放foo2引用,您需要实际调用该引用的释放,而不是引用的副本。

3
ClassOne *pointer = [[ClassOne alloc]init]; 

变量pointer是指向类ClassOne的对象的指针。它分配了新创建的对象的值。

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

*foo*foo2ClassTwo类的对象。仅为foo分配了一个新创建的对象。 foo2可能指向任何东西,因此在为其分配值之前使用它是不安全的。

foo2 = [foo add: pointer]; 

foo2被分配一个值:我假定ClassTwo类的add:消息创建一个对象(该方法的签名应-(ClassTwo*)add:(ClassOne*);

[foo release]; 

目的通过foo指向不更长的需要。

foo = foo2; 

可变foo被分配的foo2值:都指向同一个对象。

[pointer release]; 

pointer指向的对象不再需要。

[foo release]; 

目的指向foo(并且还通过foo2)不再需要。

+0

实际上,该方法的签名应该是 - (ClassTwo *)add:(ClassOne *); - 在他/她的例子中,接收者是ClassTwo的一个实例,而不是类本身。 – 2009-02-10 05:37:54

1

从你简单的例子来说,很难说出发生了什么。通常[class add:]类型方法返回void,因此它们应该引发一个编译器警告,'void value不会被忽略。

因此,如果没有更多的信息,有点难以弄清楚。

有几件事情要记住:

  • 您可以在objc发送命令到“零”。所以,如果[foo add:pointer]返回nil,那么你可以整天调用'release'而不会有任何影响。

  • retainCount是你的朋友。您可以在任何NSObject上调用它以查看有多少个对象持有它。这也可能有助于你追踪这个问题。

  • 最后是垃圾回收吗?

1

这真的取决于[foo add:pointer];的作用。看起来它会给出副本foo并保留它。这显然是不好的设计,因为如果返回的对象是复制/引用,它应该从方法中显而易见。名为add:的方法不应该给出副本。

循序渐进:

// this somehow creates a retained copy of foo. 
foo2 = [foo add:pointer]; 

// foo is released and gets destroyed. 
[foo release]; 

// makes foo point to the same object as foo2 
// (`foo` has no connection to the former object anymore) 
foo = foo2; 

// foo (and foo2 as they point to the same object) are released 
[foo release]; 
22

使用Objective-C Cocoa,我们正在使用半自动引用计数内存管理。为对象分配内存,保留对象或在对象上调用方法时,保留计数(引用计数)会增加1.在对象上调用release时,它会将保留计数递减1。当在一个对象上调用autorelease时,release将在将来的某个时刻在对象上被调用(在主运行循环期间,当你自己的代码没有执行时,所以它不会从你的下拉引用该引用)试图使用它)。当保留计数达到0时,可以释放该对象。

一般情况下,如果你的对象上调用retain,你信令的利益在里面,当你不再感兴趣的对象,你有责任在某些时候作出releaseautorelease通话。同样,如果您在对象上调用alloccopy方法,则表明您对该对象感兴趣,并且必须将其与匹配的某个位置的releaseautorelease相匹配。

这个环节几乎涵盖准则苹果使用(你应该使用)的内存管理:Simple rules for memory management in Cocoa

让我们通过代码逐行:

ClassOne *pointer = [[ClassOne alloc]init]; 

pointer点到一个新分配的ClassOne对象,保留计数为1,因为我们对它调用了alloc。我们有责任在未来的某个时间拨打releaseautoreleasepointer

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

foo指向一个新分配ClassTwo对象,为1的保留计数,因为我们调用它的alloc。我们有责任在的某个时间点拨打releaseautorelease

foo2现在没有指出任何特别的东西。这是不安全的使用。

foo2 = [foo add: pointer]; 

pointer已添加到foo(这意味着什么,我们不知道实现)。 foo可能在pointer上调用retain来表示它对其的兴趣,并将其添加为字段,或者它可能已将pointer添加到集合中(在这种情况下,集合有责任在添加对象时调用retain,并且release当一个对象被删除)。无论如何,它不会影响我们的代码块,所以我们不在乎发生了什么事情

此方法返回的引用本身可能是pointer本身,或者它可能是pointer的自动发布副本;我们无法访问API或实现来告诉我们哪一个。

在这两种情况下,我们都没有责任在此对象上调用release。如果该方法的名称中含有copy,或者如果我们在返回的引用(如foo2 = [[foo add:pointer] retain];)上调用了retain,则保留计数将增加1,并且我们有责任调用releaseautorelease

[foo release]; 

通过foo引用的对象已被释放,这意味着它的保留计数已递减1。在这个例子中,这对与alloc电话,我们在第2行作出,所以保留计数将降至0,使foo有资格被释放。一般来说,我们并不在乎对象是否已经被释放;否则,我们并不关心对象是否被释放。我们只需确保我们配对的电话号码与或autorelease呼叫的号码相同即可呼叫alloc,copyretain。如果我们随时注册一个对象的兴趣,我们有责任释放我们的兴趣,否则我们会有内存泄漏。

foo = foo2; 

foo现在指向由foo2引用同一个对象。记住,当我们得到foo2时,我们还没有调用alloccopy方法,我们也没有通过调用retain来注册它的兴趣。由于我们没有责任拨打foo2致电release,因此我们没有责任致电 foo

[pointer release]; 

pointer的保留计数已被1.本递减可能带来其保留计数为0与否,这取决于它的时候我们增加了它什么foo没有。不过,我们不在乎;我们已完成对pointer的责任,拨打电话release即可与我们在开始时致电alloc的电话相匹配。虽然pointer可能在这次调用之后仍然存在,但是我们不能做出这样的假设,并且试图对先前由指针引用的对象做任何事情都是错误的(尽管我们可以将pointer更改为指向其他任何东西)。

[foo release]; 

如果此代码的作者一直关注苹果的内存管理公约,那么这是不必要的。我们没有责任在foofoo2上拨打release(他们指向相同的对象,请记住)。这不会导致代码破坏;调用nil引用中的任何内容本质上都是空操作。但是,任何人查看代码可能会造成混淆。

现在,此代码的作者可能已经打破了内存管理约定。他可能会在add呼叫中返回pointer的副本,而不会调用autorelease,在这种情况下,呼叫方会负责呼叫release。这是一种非常糟糕的形式,如果您遇到违反内存管理约定的代码,请记录您使用它的方式以及它如何打破惯例以避免将来混淆。

1

哇,谢谢大家的好评!

我想我真正的意思是指对象而不是指针。尽管如此,我猜附加的, *foo2驻留在与foo相同的内存中。另外foo2在foo的同时从内存中释放。我还有很多东西需要倾斜,但一天只有一天!