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))
),它看起来很明显正确,你不知道为什么您正在读取垃圾值或段错误。