Swift 3 中可能存在多维数组隐式桥接错误?

Possible Bug in Swift 3 with Implicit Bridging of multidimensional arrays?

我目前正在开发用 Swift 3.

编写的 SpriteKit 游戏

当我尝试生成 Swift 的 SKShapeNodes from points stored in a multidimensional array, using the init(splinePoints: UnsafeMutablePointer, count: Int) initialiser, I found that I just was not able to use implicit bridging 功能时,即通过在相关变量前面添加“&”,将其作为 UnsafeMutablePointer 传递给函数。

这是在 Xcode Playgrounds 中重现错误(包括错误)的图像:

错误显示为

Cannot convert value of type '[CGPoint]' to expected argument type 'CGPoint'

,尽管初始化程序引用明确指出它需要一个指向 CGPoint 数组的指针。即使在 Apple provided example 部分,

部分

从点创建形状节点,就是这样完成的:

var points = [CGPoint(x: 0, y: 0),               
              CGPoint(x: 100, y: 100),               
              CGPoint(x: 200, y: -50),               
              CGPoint(x: 300, y: 30),               
              CGPoint(x: 400, y: 20)]         
let linearShapeNode = SKShapeNode(points: &points,                                   
                                  count: points.count)          
let splineShapeNode = SKShapeNode(splinePoints: &points,                                   
                                  count: points.count)

So my question is why it is not working, whether it is a compiler bug, and if not, how I can fix it.

但是请注意,在函数内部创建数组的副本,就像这样...

func createPressurePointShape(arm: Int, finger: Int) {
    var tempPoints = pointsArray[arm][finger]
    let shapeNode = SKShapeNode.init(splinePoints: &tempPoints, count: pointsArray[arm][finger].count)
}

...对我来说不是解决方案,因为在我的实际应用程序中,变量只会存在 1 帧,然后消失或造成内存泄漏。尽管这暗示了 Swift 或 Xcode 中的错误而不是我的应用程序中的错误,但这让错误消失了,但我不确定。

提前感谢您的帮助。这是代码示例,如果有人想玩它:

//: Playground - noun: a place where people can play

import UIKit
import SpriteKit

let armCount = 4
let fingersPerArm = 4
let pressurePointsPerFinger = 3

private var pointsArray = [[[CGPoint]]](repeating: [], count: Int(armCount))

for armIndex in 0...Int(armCount - 1) {

    for fingerIndex in 0...(fingersPerArm - 1) {

        pointsArray[armIndex].append([])
        for innerIndex in 0..<pressurePointsPerFinger {
            pointsArray[armIndex][fingerIndex].append(CGPoint(x: 0.5, y: 0.5))
        }
    }
}

let exampleArm = 1
let exampleFinger = 1

func createPressurePointShape(arm: Int, finger: Int) {
    let shapeNode = SKShapeNode.init(splinePoints: &pointsArray[arm][finger], count: pointsArray[arm][finger].count)
}

createPressurePointShape(arm: exampleArm, finger: exampleFinger)

您可能遗漏了压力计数(每个手指),这是一个伪代码:

let examplePressuePointsPerFinger = 1

func createPressurePointShape(arm: Int, finger: Int, pressurePoint: Int) {
    let shapeNode = SKShapeNode.init(splinePoints: &pointsArray[arm][finger][pressurePoint], count: pointsArray[arm][finger].count)
}

createPressurePointShape(arm: exampleArm, finger: exampleFinger, pressurePoint: examplePressuePointsPerFinger)

尝试像这样指向数组的第一个元素。

var finger = [CGPoint(x: 0.5, y: 0.5), CGPoint(x: 0.5, y: 0.5), CGPoint(x: 0.5, y: 0.5)]
var arm = [finger, finger, finger, finger]
var pointsArray = [arm, arm, arm, arm]

let exampleArm = 1
let exampleFinger = 1

func createPressurePointShape(arm: Int, finger: Int) {
  let shapeNode = SKShapeNode.init(splinePoints: &pointsArray[arm][finger][0], count: pointsArray[arm][finger].count)
}

createPressurePointShape(arm: exampleArm, finger: exampleFinger)

你说得很对,这看起来确实像一个错误。我有 filed a report here。一个更小的例子是:

func foo(_ ptr: UnsafeMutablePointer<Int>) {}

var arr = Array(repeating: Array(0 ..< 5), count: 5)

let i = 0
foo(&arr[i]) // Cannot convert value of type '[Int]' to expected argument type 'Int'

然而,我会对将 &pointsArray[arm][finger][0] 传递给 SKShapeNode 的初始化程序的建议保持警惕——它 碰巧 工作,因为编译器只是通过数组中第一个元素的地址,但我不认为这是有保证的。编译器将第一个元素复制到临时文件中,传递该临时文件的地址,然后再写回数组同样有效。这将在初始化程序中调用未定义的行为

相反,我只是在 SKShapeNode 上创建一个方便的初始化程序,以便首先避开错误并使用更好的 API:

extension SKShapeNode {

    convenience init(splinePoints: inout [CGPoint]) {
        self.init(splinePoints: &splinePoints, count: splinePoints.count)
    }
}

func createPressurePointShape(arm: Int, finger: Int) {
    let shapeNode = SKShapeNode(splinePoints: &pointsArray[arm][finger])
}

inout 仍然不能完全令人满意,因为我不相信传递的内存曾经发生过突变(实际上这是 SKShapeNode 的 API 的错误)。然而,如果 inout 被拿走,一个临时的可变变量被传递给 init(splinePoints:count:),数组的缓冲区将总是首先被复制(因为它需要被唯一引用),这可能是不可取的。