简体   繁体   中英

A mutable pointer to a row in a two-dimensional array

Assume there is a two-dimensional array

var myArray: [[MyClass]]

Since array is a value type in Swift, assignment like

let line = myArray[lineNumber]

will create a copy, instead of referring to an actual row.

In order to modify rows on an array (like inserting / deleting elements in a row) without copying elements and avoiding abundant use of indexing (ie having myArray[lineNumber] many times in the code), I tried C-style approach with pointers.

Assignment

let rowPointer = UnsafePointer(myArray[rowNumber])

doesn't appear to work the expected way, referring to myArray[rowNumber][0] rather than to a whole row.

However,

let rowPointer = UnsafePointer(myArray) + rowNumber

compiles without error and does the trick, so this way the whole row can be accessed by rowPointer.pointee

Since I also need to modify the row by inserting / appending elements, I tried mutable pointer:

let rowPointer = UnsafeMutablePointer(myArray) + rowNumber

However this assignment gives a compilation error:

Cannot invoke initializer for type 'UnsafeMutablePointer<_>' with an argument list of type '([[MyClass]])'

Is there a way to access a row by a mutable pointer?

What you're really tryign to achieve is add support for L values. Values that can be assigned to or edited in place. In Swift, that's achieved with the subscript operator:

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: "],\n\t[")

        return """
            Matrix(width: \(rows.count), height: \(height), rows:
                [\(rowText)]
            )
            """
    }
}

var matrix: Matrix = [
    ["A", "B"],
    ["C", "D"],
]

print(matrix)

matrix[0, 1] = "X"

print(matrix)

I would strongly recommend this over the unsafe pointer approach, which can lead you to all kinds of dangling pointer or buffer overflow issues. After all, "Unsafe" is right there in the name!

Here is another simple version to show matrix with mutable [[Any]].

  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

This is more important.

  let line1 = matrix[0] // here `let` again
  line1[2] = 33444

  print(matrix)  //matrix has changed. That means it is using reference. 

Hope it's useful.

Here is how you can modify the actual pointer of your array but I am not sure if this has any real value over the existing array copy. Since, value types are always copied, so we get the pointer, change value and assign new value to the pointer.

But, it is better that you do it in usual way, considering array as a value type. Also if your class is reference type, it simplifies things a lot.

var myArray: [[Int]] = [[1, 2, 3], [4, 5, 6]]

withUnsafeMutablePointer(to: &myArray) { pointer  in
    print(pointer)
    var pointee = pointer.pointee
    pointee[1][1] = 10
    pointer.pointee = pointee
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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