让我们从 MRC(手动引用计数)的角度来考虑这个问题。当您从函数返回堆栈块时,返回它的副本始终是正确[[ copy] autorelease]
的做法(通常使用)。这是因为堆栈块的生命周期是结束的函数调用。所以为了让返回的指针在返回后指向一个有效的块,当然要返回一个堆副本。
但是,如何将块作为参数传递给函数或方法。你应该传递它的副本吗?这有点复杂。我们知道,如果您需要将块存储在比函数调用寿命更长的地方(例如,在实例变量或全局变量中等),以供以后使用,您需要存储块的副本。但是我们怎么知道这是真的(如果是真的,哪个函数(调用者/被调用者)负责复制它)?
一般来说,当你将它传递给参数为块类型的函数或方法时,你不需要复制它,因为作为一个接受块参数的函数,如果需要,该函数负责复制它把它储存起来以备后用。
但是将块传递给参数是非块类型(如id
)的函数或方法呢?然后那个函数不知道它的参数是一个块,所以它只会保留对象,如果它需要存储它以供以后使用,而不是复制它。在这种情况下,如果我们知道函数或方法将存储对象以供以后使用(例如 with -[NSMutableArray addObject:]
),我们应该传递块的副本。我们应该总是传递一个副本吗?不必要。如果我们知道函数或方法不会为以后存储块(例如只打印对象),那么复制块是低效的。
在 ARC 中,编译器会尽力为您做尽可能多的事情。所以在返回一个堆栈块(“向上传递”)的情况下,编译器知道返回一个副本总是正确的,所以它隐式地这样做了。这就是为什么你不再需要自己做。
但是,对于将块传递给方法或函数(“向下传递”),并不总是可以自动确定是否需要复制,并且额外的复制可能效率低下,因此编译器不会自动执行任何操作; 你程序员需要弄清楚。
我注意到在最近版本的编译器中,ARC 在我认为的更多情况下复制块,所以也许他们将 ARC 的实现更改为几乎总是复制块,以便在谨慎方面犯错,并且他们认为性能成本并不那么重要。因此,它现在可能会直接将块传递给addObject:
而无需显式复制。但我相信 ARC 并非总是如此,并且语言不能保证这一点,或者这种行为将来会继续存在。