最近我在头文件和文档中寻找其他内容时,发现了以下函数原型:<objc/objc-auto.h>
:
void *objc_memmove_collectable(void *dst, const void *src, size_t size);
这是一组函数中的一个,在注释“写屏障。编译器使用”后面。这些函数用于通知垃圾回收器管理的内存已被修改,以便它可以扫描引用并不会意外地回收应由强引用保留但其不知道的内存。我知道适当使用
__strong
几乎总是会导致编译器插入正确的函数,但我也听到过苹果工程师说有极少数情况下必须直接调用这些函数。(我认为这是在文件未编译为Objective-C时发生的,例如写入GC管理的内存的C代码,但我不确定。)因此,苹果的Objective-C Runtime Release Notes for Mac OS X v10.5文档对该函数有以下说明:(第一个标题下的最后一段)
“在大量复制内存进入垃圾收集块时,您必须使用API
objc_memmove_collectable(void *dst, const void *src, size_t size)
。”该函数似乎是针对将非GC内存中的项目移动到GC内存的,电子邮件讨论档案似乎表明其目的仅是为了触发单个写入屏障以进行大块复制。(例如,1000个单独的写入带有每个写入屏障,与1个批量复制带有整个内存区域的单个写入屏障相比。)这是必须使用的一种情况,但文档没有说明何时不应该(或不需要)使用它。
例如,我有一个使用
NSAllocateCollectable()
和NSScannedOption
分配的内存块,并将其用作动态扩展的循环缓冲区。如果缓冲区变满,我会使用NSReallocateCollectable()
和NSScannedOption
将其大小加倍。环绕的部分(在数组中的第一个插槽和缓冲区中的最后一个对象之间)将被复制/移动到数组的第二半部分的开头。然后,我使用bzero()
清除从中复制数据的插槽,以避免过度根据已移动的对象进行根处理。(请参见this file中的460-467行。是的,代码可以正常运行 - 它已经完全通过单元测试,自从我一段时间前添加了__strong
属性以来,我没有看到任何崩溃。)问题:
什么时候需要使用
objc_memmove_collectable()
而不是memmove()
或memcpy()
?例如,如果源和目标都是GC管理的内存,会怎样?(我的内存声明为__strong id *array;
,所以我猜编译器会插入屏障。)如果不需要,使用它会帮助/阻碍GC性能吗?(例如,它是否持有任何类型的锁,或者帮助GC避免手动扫描?)这被认为是良好/差劲的风格吗?
编辑: 由于memcpy
和memmove
完全不涉及GC,我想知道为什么我没有看到任何由于内存被收集而导致的崩溃。我目前的最佳猜测是,由于bzero
也不会告诉GC任何信息,因此收集器在下一次扫描整个内存块之前不会发现清零的内存和移动的数据。如果收集器仍然将现在清零的引用视为根,并且尚未计算新的内存位置,那么这就解释了为什么值不会过早地被收集。这听起来正确吗?