新手呼叫目标C和斯威夫特。我使用以下代码从C Float数组创建一个NSArray:
float* output_f = output.data_ptr<float>();
NSMutableArray *results = [NSMutableArray arrayWithCapacity: 1360*1060]
for (int i = 0; i < 1360 * 1016; i++) {
[results insertObject:@(output_f[i]) atIndex:i];
}
但是,由于要插入100多万个样本,因此速度很慢,并且成为我的应用程序的瓶颈。有没有一种更快的方法可以从C数组创建NSArray,而不需要逐个复制元素?
2条答案
按热度按时间az31mfrm1#
不需要通过OBJ-C。假设
output_f
出现在通过桥接头包含的包含文件中,则SWIFT会将其类型视为UnsafeMutablePointer<CFloat>
(CFloat
只是Float
的typealias
,命名是为了澄清它对应于C类型)。假设您还使数组中的浮点数可用,假设桥接头文件中的某个位置包含:
然后在SWIFT端,您可以这样使用它们:
将
output_f_count
转换为Int
是必要的,因为SWIFT将C的int
解释为CInt
(也称为Int32
)。您可以像使用数组一样使用
UnsafeMutablePointer
,但不能复制。它只是SWIFT中C数据的别名。如果您希望确保不会更改数据,则可以创建一个
UnsafeBufferPointer
,但您需要强制转换指针。由于没有复制,这两个选项都非常快。然而,它们只是一些指针。如果SWIFT修改了内容,C代码将看到更改的数据,反之亦然。这可能是一件好事,也可能不是一件好事,这取决于您的用例,但您肯定想要意识到这一点。
如果您想制作副本,可以非常轻松地制作SWIFT阵列,如下所示:
现在,您在
Array
中拥有了您的快捷副本。作为非常密切相关的事情,如果在C标头中将
output_f
声明为如下所示的实际数组,那么斯威夫特就看不到指针了。信不信由你,它看到的是一个元组...包含大量
CFloat
成员的又大又丑的元组,它具有作为值类型的好处,但很难直接使用,因为您不能对其进行索引。幸运的是,您可以解决这个问题:RandomAccessCollection
方法,如map
、filter
、forEach
等。更新:
OP在评论中说,他尝试过这种方法,但在取消对它们的引用时获得了EXEC_BAD_ACCESS。缺少的是从
output
获取指针到SWIFT可用之间发生的事情的上下文。根据前面的线索,它实际上是C++,我认为
output
可能是std::vector<float>
,它可能在SWIFT对指针执行任何操作之前就超出了范围,所以它的析构函数被调用,当然,这会删除它的内部数据指针。在这种情况下,SWIFT正在访问不再有效的内存。有两种方法可以解决这个问题。第一个是确保
output
在Swift
处理完它的数据之前不会被清理。另一种选择是用C语言复制数据。现在,可以在SWIFT访问
output_f
之前清理output
。这也使得最初询问的副本比使用NSArray
快得多。假设C代码在此之后不使用output_f
,则SWIFT可以直接获得它的所有权。在这种情况下,SWIFT需要确保在完成时调用free(outout_f)
。如果SWIFT代码不关心它是否在实际数组中,
Unsafe...BufferPointer
类型就可以完成这项工作。然而,如果需要实际的
Array
,这将是另一个拷贝,如果可以避免的话,仅仅为了将相同的数据拷贝到SWIFTArray
中而拷贝两次是没有意义的。如何避免它取决于C(或Obj-C)是调用SWIFT,还是SWIFT调用Obj-C。我将假设它正在调用C。因此,让我们假设SWIFT正在调用一些定义如下的C函数get_floats()
:您希望更改接口,以便将预分配的指针及其容量作为参数提供。
在SWIFT端,您可以分配指针,但因为您无论如何都希望它位于
Array
中:最后一件事。上面的代码假设
output.data_ptr()
指向至少capacity
的浮点数。你确定这是真的吗?假设output
为std::vector
,最好将memcpy
调用更改为:这确保了如果实际数据小于
capacity
,则不会从实际数据的末尾读取垃圾数据。然后,您可以从get_floats
执行return floatsToCopy;
。然后在SWIFT方面,看起来是这样的:
您实际上不必使用
keepingCapacity
参数,但是这样做允许您重用数组,而不必为更多的内存分配买单。在使用相同的数组再次调用get_floats
之前,只需重新填充到满容量即可。此外,除非您的内存使用高峰是个问题,否则keepingCapacity: true
可能会比缺省值更快,至少不会更差,因为如果没有它,Array
可能会选择重新分配到更小的大小,这在内部是一个分配、一个副本和一个空闲空间,整个问题的关键是避免副本...但是动态内存分配是非常慢的部分。给定CPU缓存和指令流水线的工作方式,您可以在进行一次内存分配所需的时间内进行大量顺序复制。vsmadaxz2#
根据评论部分,您的最终目标是读取SWIFT中的C-ARRAY数据。如果您知道数组的长度,则可以将其作为指针从Objective-C函数返回:
只需从SWIFT的
UnsafePointer
中读取:使用完毕后,不要忘记释放指针: