简体   繁体   English

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

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

I am currently working on a SpriteKit game written in Swift 3. 我目前正在研究用Swift 3编写的SpriteKit游戏。

When I tried generating 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 feature of Swift, namely by adding a "&" in front of the var in question, to hand it to the function as an UnsafeMutablePointer. 当我尝试从存储在多维数组中的点生成SKShapeNodes时 ,使用init(splinePoints:UnsafeMutablePointer,count:Int)初始化器,我发现我只是无法使用Swift的隐式桥接功能,即添加“&”在有问题的var前面,将它作为UnsafeMutablePointer传递给函数。

Here is an image of the reproduction of the bug, including the error, in Xcode Playgrounds: 这是Xcode Playgrounds中错误再现的图像,包括错误:

在Playground中复制bug

The error reads 错误读取

Cannot convert value of type '[CGPoint]' to expected argument type 'CGPoint' 无法将'[CGPoint]'类型的值转换为预期的参数类型'CGPoint'

, although the initialiser reference explicitly states that it wants a pointer to the CGPoint array. 虽然初始化引用明确声明它需要一个指向CGPoint数组的指针。 Even in the Apple provided example , section 即使在Apple提供的示例中 ,也是如此

Creating Shape Nodes from Points , it is done exactly that way: 从Points创建形状节点 ,就是这样完成的:

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. 所以我的问题是为什么它不起作用,是否是编译器错误,如果不是,我如何解决它。

Please note, however, that creating a copy of the array inside the function, like this... 但是请注意,在函数内部创建数组的副本,就像这样......

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

...is not a solution for me, because in my actual application the variable would only exist for 1 frame, and either be gone or create a memory leak afterwards. ...对我来说不是一个解决方案,因为在我的实际应用中,变量只存在1帧,然后消失或者之后产生内存泄漏。 Just the fact that this lets the error go away though hints towards a bug in Swift or Xcode rather than my app, but I am unsure about it. 事实上这可以让错误消失,虽然暗示了Swift或Xcode中的错误,而不是我的应用程序,但我不确定它。

Thank you for your help in advance. 提前谢谢你的帮助。 Here's the code example, if somebody would like to play around with it: 这是代码示例,如果有人想玩它:

//: 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)

You're quite right, this does indeed look like a bug. 你说得对,这确实看起来像个bug。 I have filed a report here . 在这里提交了一份报告 A more minimal example would be be: 一个更小的例子是:

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'

I would however be wary of the suggestion to pass &pointsArray[arm][finger][0] to SKShapeNode 's initialiser – it happens to work because the compiler is just passing the address of the first element in the array, but I don't believe that's guaranteed. 然而,我会谨慎地将&pointsArray[arm][finger][0]传递给SKShapeNode的初始化器 - 这恰好可行,因为编译器只是传递了数组中第一个元素的地址,但我不知道相信这是有保证的。 It would be equally valid for the compiler to copy the first element into a temporary, pass the address of that temporary, and then write-back to the array afterwards. 编译器将第一个元素复制到临时元素,传递该临时元素的地址,然后写回数组同样有效。 That would invoke undefined behaviour in the initialiser. 这将在初始化器中调用未定义的行为

Instead, I would just create a convenience initialiser on SKShapeNode in order to both side-step the bug in the first place and work with a nicer API: 相反,我只是在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])
}

The inout still isn't fully satisfactory though as I don't believe the passed memory is ever mutated (really that's a bug with SKShapeNode 's API). inout仍然是不能令人完全满意,虽然我不相信通过内存不断突变(真的是一个错误SKShapeNode的API)。 However, if inout was taken away and a temporary mutable variable was passed instead to init(splinePoints:count:) , the array's buffer would always be copied first (as it needs to be uniquely referenced), which may not be desireable. 但是,如果inout被取消并且临时可变变量被传递给init(splinePoints:count:) ,则数组的缓冲区将始终首先被复制(因为它需要被唯一引用),这可能是不可取的。

Try pointing to the first element of the array like this. 尝试像这样指向数组的第一个元素。

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)

You are probably missing a pressure count ( per finger), here is a pseudo code: 您可能错过了压力计数(每个手指),这是一个伪代码:

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM