1) 当我在主程序中调用 [result release] 时,位于函数内的 *sumFrac 是否被释放?
这是一个棘手的问题。
sumFrac它本身只是一个指向对象的指针。当你复制一个指针时——无论是通过直接赋值=、作为参数传递、从函数返回等——这只是复制一个指针。因此,result是指向与 相同的对象的指针sumFrac。所以,当你发送它时release,它就像发送一个发布到sumFrac.
(如果您使用 ARC,这实际上会变得更加复杂,因为有时仅复制指针会自动执行 refcount 的工作。但它也成为编译器的问题,而不是您的问题,只要您使用正确的名称或属性。)
但是,您反复为 分配一个新值sumFrac。每次执行此操作时,它都会停止指向旧值。没有什么release是旧值,也不retain是新值。因此,您返回的值sumFrac 确实被释放了——但是,除非arrayLength是 0,否则这与您在第一行中分配的值不同。我什至不确定你为什么要分配一个Fraction对象,而你只是要忽略它以支持另一个对象,但如果你想让这个工作,你可以:
for ( fractsPtr = fracArray; fractsPtr < arrayLength; ++fractsPtr )
[sumFrac release];
sumFrac = [[*fractsPtr add: *(fractsPtr + 1)] retain];
现在,每次你 reassignsumFrac时,你首先释放旧对象并保留新对象,所以每个对象都Fraction以相同数量的引用结束(包括无用的新对象有 0 个引用),除了你返回的那个有一个额外的 ref,稍后release将在 main 函数中使用。(我在这里假设它-[add:]返回一个新的 autoreleased Fraction。如果它改为修改对象,或者返回一个新的 +1Fraction或其他东西,那就是另一回事了。)
如果您很难保持引用计数正常,您可能需要考虑遵循标准命名约定和/或使用自动释放池和/或 ARC。(值得注意的是,您已经有一个自动释放池。)或者,或者,使用 Leaks 和 Xcode 附带的其他工具来观察事物,或者添加转储引用计数的代码,以便以后查看它们。
2) 当我尝试使用 [fractionArray release] 释放 Fraction 数组对象时,为什么会收到编译器错误?
因为fractionArray是一个Fraction*[]——也就是说,一个 C 风格的Fraction对象数组,而不是一个Fraction对象。您不能将消息发送到 C 样式数组,只能发送到对象。
事实上,您在这里看到的错误或警告(取决于您的编译器设置)已经解释了这一点:
frac.m:61:6: warning: receiver type 'Fraction **' is not 'id' or interface pointer,
consider casting it to 'id'
[fractionArray release];
为了理解这一点,您必须知道 anid是一种表示“指向任何 ObjC 对象的指针”的类型,并且指向对象指针的指针与指向对象的指针不同,因此可能不是对你来说已经足够了。但是,如果您发布了错误消息而不是仅仅说“我遇到了编译器错误”,您就会立即得到答案。
如果你想以这种方式做事,你必须发送release到数组中的每个Fraction对象。您已经在使用[a release]等。您根本不需要对数组做任何事情,因为它是在堆栈上分配的(如果您已经分配了它,例如,malloc您会想要free它,不是release。)所以,你完成了。
但是更好的方法是创建一个 ObjC 数组,它是一个对象:
NSArray *fractionArray = [NSArray arrayWithObjects: a, b, c, nil];
// ...
[fractionArray release];
(除非在那种情况下,你实际上并不想要release它,因为arrayWithObjects:返回一个自动释放的数组,并且你有一个自动释放池,所以不要妨碍池。)
3) 最后,该函数实际上并没有返回一个和,而是 0/0。为什么会这样?
让我们看看你的循环:
for ( fractsPtr = fracArray; fractsPtr < arrayLength; ++fractsPtr )
如果你打算做 C 风格的事情,你会开始遇到 C 错误而不是 ObjC 错误……你正在比较fractsPtr < arrayLength. 这会将两者转换为可比较的类型,然后比较它们。fractsPtr是指向堆栈上的数组的指针,因此显然不小于 2。您需要执行以下操作之一:
for ( fractsPtr = fracArray; fractsPtr < fracArray+arrayLength; ++fractsPtr )
… 或者
for (i = 0; i < arrayLength; ++i) {
fractsPtr = fracArray[i];
如果编译时没有警告,我会感到非常惊讶。我自己试了一下,我明白了:
frac.m:34:44: warning: ordered comparison between pointer and integer
('Fraction **' and 'int')
for ( fractsPtr = fracArray; fractsPtr < arrayLength; ++fractsPtr )
警告是有原因的。如果我看到这个,我会立即找到答案,而不是浪费时间试图猜测add:可能在做什么等。即使警告没有向你解释问题,至少在你的问题中提到它。
如果你解决这个问题,会发生什么?好吧,每次通过循环,它都会分配sumFracs给[*fractsPtr add: *(fractsPtr+1)];只有最后一个分配很重要。因此,它仍然不会对所有分数求和,而只是对最后两个分数求和——换句话说,它将与[fracArray[arrayLength-1] add:fracArray[arrayLength]].
此外,使用命名参数arrayLength实际上意味着比数组长度小 1 是灾难的邀请。你知道打电话arraySum(fractionArray, 2)是因为你刚刚写了代码,但是当你在几周后回到它时,你会写arraySum(fractionArray, 3)(或arraySum(fractionArray, sizeof(fractionArray)/sizeof(*fractionArray))),它看起来很明显正确,你不知道为什么您正在读取垃圾值或段错误。