Возможная ошибка в Swift 3 с неявным мостовым соединением многомерных массивов?
В настоящее время я работаю над игрой SpriteKit, написанной на Swift 3.
Когда я попытался сгенерировать SKShapeNodes из точек, хранящихся в многомерном массиве, используя инициализатор init(splinePoints: UnsafeMutablePointer, count: Int), я обнаружил, что просто не смог использовать неявную функцию моста Swift, а именно, добавив "&" перед рассматриваемой переменной, чтобы передать его функции как UnsafeMutablePointer.
Вот изображение воспроизведения ошибки, включая ошибку, в Xcode Playgrounds:
Ошибка читает
Не удалось преобразовать значение типа "[CGPoint]" в ожидаемый тип аргумента "CGPoint"
, хотя ссылка инициализатора явно заявляет, что она хочет указатель на массив CGPoint. Даже в приведенном Apple примере, раздел
Создание узлов формы из точек выполняется именно так:
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)
Итак, мой вопрос, почему он не работает, является ли это ошибкой компилятора, и если нет, как я могу это исправить.
Обратите внимание, однако, что создание копии массива внутри функции, как это...
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)
3 ответа
Вы совершенно правы, это действительно похоже на ошибку. Я подал отчет здесь. Более минимальным примером будет:
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:)
, буфер массива всегда будет копироваться первым (так как на него нужно ссылаться однозначно), что может быть нежелательно.
Попробуйте указать на первый элемент массива следующим образом.
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)
Возможно, вам не хватает подсчета давления (на палец), вот псевдокод:
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)