#arrays #swift #pointers
#массивы #swift #указатели
Вопрос:
Предположим, что существует двумерный массив
var myArray: [[MyClass]]
Поскольку array является типом значения в Swift, присваивание типа
let line = myArray[lineNumber]
создаст копию вместо ссылки на фактическую строку.
Чтобы изменять строки в массиве (например, вставлять / удалять элементы в строке) без копирования элементов и избегая чрезмерного использования индексации (т. Е. многократного использования myArray[lineNumber]
в коде), я попробовал подход в стиле C с указателями.
Назначение
let rowPointer = UnsafePointer(myArray[rowNumber])
похоже, что он работает не ожидаемым образом, ссылаясь на myArray[rowNumber][0]
, а не на целую строку.
Однако,
let rowPointer = UnsafePointer(myArray) rowNumber
компилируется без ошибок и выполняет трюк, так что таким образом доступ ко всей строке может быть получен rowPointer.pointee
Поскольку мне также нужно изменить строку путем вставки / добавления элементов, я попробовал изменяемый указатель:
let rowPointer = UnsafeMutablePointer(myArray) rowNumber
Однако это присвоение приводит к ошибке компиляции:
Невозможно вызвать initializer for type 'UnsafeMutablePointer<_>' with an argument list of type '([[MyClass]])'
Есть ли способ получить доступ к строке с помощью изменяемого указателя?
Ответ №1:
Вот как вы можете изменить фактический указатель вашего массива, но я не уверен, имеет ли это какое-либо реальное значение по сравнению с существующей копией массива. Поскольку типы значений всегда копируются, поэтому мы получаем указатель, изменяем значение и присваиваем указателю новое значение.
Но лучше сделать это обычным способом, рассматривая array как тип значения. Кроме того, если ваш класс имеет ссылочный тип, это многое упрощает.
var myArray: [[Int]] = [[1, 2, 3], [4, 5, 6]]
withUnsafeMutablePointer(to: amp;myArray) { pointer in
print(pointer)
var pointee = pointer.pointee
pointee[1][1] = 10
pointer.pointee = pointee
}
Комментарии:
1. Отлично! В моем случае мне нужно получить доступ к определенной строке. Поэтому я помещаю: withUnsafeMutablePointer(to: amp;myArray[номер строки]) {указатель на … } где (я надеюсь) pointer.pointee на самом деле является конкретной строкой в массиве, поэтому я использую pointer.pointee.insert(..) и pointer.pointee.append(…). У меня не было возможности посмотреть, как это работает, но должно быть все в порядке, учитывая, что он компилируется без ошибок.
Ответ №2:
Чего вы действительно пытаетесь достичь, так это добавить поддержку значений L. Значения, которые могут быть присвоены или отредактированы на месте. В Swift это достигается с помощью оператора подстрочного индекса:
struct Matrix<T> {
var rows: [[T]]
init(rows: [[T]]) { self.rows = rows }
init(width: Int, height: Int, initialElement: T) {
let rows = (0..<height).map { _ in
return Array(repeating: initialElement, count: width)
}
self.init(rows: rows)
}
subscript(row: Int, col: Int) -> T {
get { return self.rows[row][col] }
set { self.rows[row][col] = newValue }
}
}
extension Matrix: ExpressibleByArrayLiteral {
init(arrayLiteral: [T]...) {
self.init(rows: arrayLiteral)
}
}
extension Matrix: CustomDebugStringConvertible {
var debugDescription: String {
let height = rows.first?.count ?? 0
let rowText = rows.map { row in
row.map { element in String(describing: element) }.joined(separator: ", ")
}.joined(separator: "],nt[")
return """
Matrix(width: (rows.count), height: (height), rows:
[(rowText)]
)
"""
}
}
var matrix: Matrix = [
["A", "B"],
["C", "D"],
]
print(matrix)
matrix[0, 1] = "X"
print(matrix)
Я бы настоятельно рекомендовал это вместо подхода с небезопасным указателем, который может привести ко всевозможным проблемам с зависанием указателя или переполнением буфера. В конце концов, «Небезопасный» прямо там, в названии!
Ответ №3:
Вот еще одна простая версия для отображения матрицы с изменяемым [[Любым]].
let row1: NSMutableArray = [1,"22",3]
let row2: NSMutableArray = [2,"33",3]
let row3: NSMutableArray = [4,"4",5]
let matrix: [NSMutableArray] = [row1, row2, row3] // Here matrix is an immutable. This is the difference from other answers.
matrix[0][1] = 22
matrix[0][2] = "23"
matrix[0][3] = "2333"
print(type(of:matrix[0][1])) // __NSCFNumber
print(type(of:matrix[0][2])) // NSTaggedPointerString
Это более важно.
let line1 = matrix[0] // here `let` again
line1[2] = 33444
print(matrix) //matrix has changed. That means it is using reference.
Надеюсь, это полезно.
Комментарии:
1. Действительно, NSMutableArray является ссылочным типом, но он не является универсальным, поэтому у вас не может быть NSMutable массива с элементами определенного типа.
2. Он универсальный, поскольку использует любой nsobject. В нем просто отсутствует проверка типа. Но когда вы решаете использовать reference, вы в некотором роде рискуете. Этот сухой класс безопаснее, чем указатель.