为什么Swift的UnsafePointer和UnsafeBufferPointer不能互换?

萨甘仪式

我已经使用Apple的神经网络工具进行了很多工作,这意味着我已经使用不安全的指针进行了很多工作。我从C成长,而我与Swift在一起工作已经有一段时间了,所以我很习惯使用它们,但是关于它们的一件事让我感到非常沮丧。

我无法弄清楚为什么要从另一种派生一种不安全的指针需要付出任何努力。总的来说,这看起来应该是微不足道的,但是不同类型的初始值设定项具体说明了它们将采用哪种输入方式,而我很难确定规则。

一个简单而具体的例子,也许是让我最难过的一个例子

// The neural net components want mutable raw memory, and it's easier
// to build them up from the bottom, so: raw memory
let floats = 100
let bytes = floats * MemoryLayout<Float>.size

let raw = UnsafeMutableRawPointer.allocate(byteCount: bytes, alignment: MemoryLayout<Float>.alignment)

// Higher up in the app, I want to use memory that just looks like an array
// of Floats, to minimize the ugly unsafe stuff everywhere. So I'll use
// a buffer pointer, and that's where the first confusing thing shows up:

// This won't work
// let inputs = UnsafeMutableBufferPointer<Float>(start: raw, count: floats)

// The initializer won't take raw memory. But it will take a plain old
// UnsafePointer<Float>. Where to get that? you can get it from the raw pointer
let unsafeMutablePointer = raw.bindMemory(to: Float.self, capacity: floats)

// Buf if that's possible, then why wouldn't there be a buffer pointer initializer for it?

// Of course, with the plain old pointer, I can get my buffer pointer
let inputs = UnsafeMutableBufferPointer(start: unsafeMutablePointer, count: floats)

尽管我无法找到有关不同种类背后的理论的任何讨论,但确实在本教程中找到了一条线索有一张图表比较了不同的类型,它说普通的旧UnsafePointer字体是可以跨越的,但不是集合,而是收藏集,UnsafeBufferPointer却不是不可跨越的。

我理解像集合这样的不可跨越的集合的概念。但是这两种类型的不安全指针都允许使用下标。它们就像常规数组一样工作,在我看来,它们都是可扩展的集合。也许那里有一个微妙的线索我想念。

为什么无法UnsafeBufferPointer从可以从中获得的类型中获得UnsafePointer

伊泰·费伯

这些类型之间的区别并不像您想象的那么大。从概念上讲,UnsafeBufferPointer等效于的元组(UnsafePointer, Int),即它是指向内存中具有已知计数的元素的缓冲区的指针。UnsafePointer相反,是指向内存中具有未知计数的元素的指针UnsafePointer更紧密地表示您可能习惯于C中的任意指针:它可能指向一个或多个元素,但仅凭其本身,就无法找出答案。

UnsafeBufferPointer拥有已知计数也意味着它能够符合Collection(这需要一个已知的开始和结束位置),而与UnsafePointer没有该信息的相对。

Swift非常是一种语义语言,非常强调在类型系统中表达有关您可用工具的知识。如您所指出的,有些操作可以对一种类型执行,而不能对另一种类型执行-这是设计使某些操作难以正确执行。

如果您有正确的信息在它们之间移动,这些指针也可以转换:

  • UnsafeBufferPointer有一个baseAddress一个UnsafePointer:给定一个缓冲区,可以随时“扔掉”有关计数,以获得潜在的无数指针
  • 给定anUnsafePointer和a count,您还可以使用以下命令表示内存中存在缓冲区UnsafeBufferPointer.init(start:count:)

普遍的答案是:使用最具体的指针类型可以代表您拥有的数据。通常,Buffer如果要指向多个元素并且知道有多少个元素,则最好使用指针变体。同样,如果您要指向内存中的任意字节(可能具有也可能没有类型),则应Raw尽可能使用指针。(当然,如果您需要写入内存中的这些位置,则也需要使用这些Mutable变量的变体。)

有关更多信息,我强烈推荐WWDC 2020上Andrew Trick的主题演讲:安全地管理Swift中的指针他详细介绍了代表Swift中指针寿命的概念状态机,以及如何在指针之间进行转换和正确使用指针类型。(涉及到主题时,它尽可能接近马的嘴!)


另外,关于示例代码:@Sweeper在注释中正确指出,如果要分配Floats的缓冲区,则不应分配原始缓冲区,然后绑定其内存类型。您不仅冒着浪费所需缓冲区大小的风险,而且还冒着不考虑填充(对于某些类型必须手动计算)的风险。

相反,您应该使用UnsafeMutableBufferPointer.allocate(capacity:)分配缓冲区,然后可以将其写入。它正确地考虑了对齐和填充,因此您不会出错。

在Swift中,原始内存和类型化内存之间的区别非常细微,Andy在链接的谈话中比我在这里描述的要好得多,但是tl; dr:您几乎永远不必手动绑定内存,并且如果将内存绑定到非平凡的类型,几乎可以肯定您做错了。(并不是说您在这里这样做,只是一个提示)


最后,以Strideablevs.Collection和下标为主题-您可以同时下标的事实与C的行为相匹配,但是在Swift中具有微妙的语义区别。

下标到某种程度上UnsafePointer意味着它在C语言中的作用:一个人UnsafePointer知道其基本类型,并使用填充和对齐方式,可以计算该类型下一个对象在内存中的位置(这就是其Strideable一致性所隐含的含义);您可以像在C语言中那样使用下标运算符来做到这一点。此外,就像在C语言中一样:由于您不知道缓冲区的结束位置,因此可以使用UnsafePointer无边界检查任意地对下标进行操作-根本就不会。不能以任何方式提前知道您要进行的访问是否有效。

另一方面,下标直通UnsafeBufferPointer就像访问内存中元素集合内部的元素。由于缓冲区的开始和结束位置有明确的界限,因此您需要进行界限检查,而对的界限进行索引UnsafeBufferPointer显然更是一个错误。按照这些思路Strideable一致性UnsafeBufferPointer就没有多大意义:类型的“跨度”Strideable表示它知道如何到达“下一个”,但是在整型之后并没有逻辑的“下一个”缓冲区UnsafeBufferPointer

因此,这两种类型都以有效执行相同操作的下标运算符结尾,但在语义上具有非常不同的含义。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

为什么Swift的UnsafePointer和UnsafeBufferPointer不能互换?

来自分类Dev

为什么不能互换std :: atomic <T>?

来自分类Dev

为什么LIB和DLL无法互换?

来自分类Dev

为什么在Swift 3中'nil'与'UnsafePointer <CGAffineTransform>'不兼容?

来自分类Dev

为什么IEEE-754浮点不能在平台之间互换?

来自分类Dev

a [n]是否真的可以和*(a + n)互换-为什么sizeof返回两个不同的答案?

来自分类Dev

为什么在C#6.0中将内部“最终”和外部“何时”的执行顺序互换?

来自分类Dev

为什么 <p> 标签可以互换地充当块元素和内联元素

来自分类Dev

Swift:为什么变异函数不能是静态的

来自分类Dev

Swift:为什么不能让懒惰的,计算的属性和属性观察器

来自分类Dev

Swift:为什么不能让懒惰的,计算的属性和属性观察器

来自分类Dev

为什么不能导入LinearLayoutManager和RecyclerView?

来自分类Dev

Swift UnsafeMutablePointer和UnsafeMutablePointer <UnsafePointer <SomeType >>

来自分类Dev

Swift:为什么没有dynamicType的非静态方法不能调用静态变量和常量(静态let)?

来自分类Dev

Swift:为什么没有dynamicType的非静态方法不能调用静态变量和常量(静态let)?

来自分类Dev

为什么在Swift中不能枚举可选数组?

来自分类常见问题

为什么我不能在Swift中使用let in协议?

来自分类Dev

Swift:为什么我不能从覆盖init调用方法?

来自分类Dev

Swift 1.2为什么我不能继承任何NSCell的子类?

来自分类Dev

为什么在Swift中不能使用UIRefreshControl?

来自分类Dev

为什么我不能在Swift中使用'object == nil'?

来自分类Dev

为什么Swift在编译代码时不能删除空格?

来自分类Dev

为什么我不能在 Swift 中自己运行命令?

来自分类Dev

为什么不能在分配中使用Unicode字符√和??

来自分类Dev

为什么我似乎不能结合:visited和:: after?

来自分类Dev

为什么我不能在MacVim中使用`<和`>?

来自分类Dev

为什么MockMVC和Mockito不能一起使用?

来自分类Dev

为什么filter和rowSums不能一起使用?

来自分类Dev

为什么'['和']'ASCII码不能互相跟随?

Related 相关文章

  1. 1

    为什么Swift的UnsafePointer和UnsafeBufferPointer不能互换?

  2. 2

    为什么不能互换std :: atomic <T>?

  3. 3

    为什么LIB和DLL无法互换?

  4. 4

    为什么在Swift 3中'nil'与'UnsafePointer <CGAffineTransform>'不兼容?

  5. 5

    为什么IEEE-754浮点不能在平台之间互换?

  6. 6

    a [n]是否真的可以和*(a + n)互换-为什么sizeof返回两个不同的答案?

  7. 7

    为什么在C#6.0中将内部“最终”和外部“何时”的执行顺序互换?

  8. 8

    为什么 <p> 标签可以互换地充当块元素和内联元素

  9. 9

    Swift:为什么变异函数不能是静态的

  10. 10

    Swift:为什么不能让懒惰的,计算的属性和属性观察器

  11. 11

    Swift:为什么不能让懒惰的,计算的属性和属性观察器

  12. 12

    为什么不能导入LinearLayoutManager和RecyclerView?

  13. 13

    Swift UnsafeMutablePointer和UnsafeMutablePointer <UnsafePointer <SomeType >>

  14. 14

    Swift:为什么没有dynamicType的非静态方法不能调用静态变量和常量(静态let)?

  15. 15

    Swift:为什么没有dynamicType的非静态方法不能调用静态变量和常量(静态let)?

  16. 16

    为什么在Swift中不能枚举可选数组?

  17. 17

    为什么我不能在Swift中使用let in协议?

  18. 18

    Swift:为什么我不能从覆盖init调用方法?

  19. 19

    Swift 1.2为什么我不能继承任何NSCell的子类?

  20. 20

    为什么在Swift中不能使用UIRefreshControl?

  21. 21

    为什么我不能在Swift中使用'object == nil'?

  22. 22

    为什么Swift在编译代码时不能删除空格?

  23. 23

    为什么我不能在 Swift 中自己运行命令?

  24. 24

    为什么不能在分配中使用Unicode字符√和??

  25. 25

    为什么我似乎不能结合:visited和:: after?

  26. 26

    为什么我不能在MacVim中使用`<和`>?

  27. 27

    为什么MockMVC和Mockito不能一起使用?

  28. 28

    为什么filter和rowSums不能一起使用?

  29. 29

    为什么'['和']'ASCII码不能互相跟随?

热门标签

归档