Search code examples
swiftunsafe-pointers

How to extract UnsafePointer<CGFloat> from UnsafePointer<CGPoint> - Swift


I’m playing around with learning about pointers in Swift.

For instance, this code starts with a CGPoint array, creates an UnsafePointer and then extracts all the x values into a CGFloat array:

import Foundation

let points = [CGPoint(x:1.2, y:3.33), CGPoint(x:1.5, y:1.21), CGPoint(x:1.48, y:3.97)]
print(points)

let ptr = UnsafePointer(points)
print(ptr)

func xValues(buffer: UnsafePointer<CGPoint>, count: Int) -> [CGFloat]? {
    return UnsafeBufferPointer(start: buffer, count: count).map { $0.x }
}

let x = xValues(buffer: ptr, count: points.count)
print(x)

And the expected output is:

[Foundation.CGPoint(x: 1.2, y: 3.33), Foundation.CGPoint(x: 1.5, y: 1.21), Foundation.CGPoint(x: 1.48, y: 3.97)]
0x0000556d6b818aa0
Optional([1.2, 1.5, 1.48])

Now I’d like to have the xValues function return directly UnsafePointer<CGFloat>, instead of going through [CGFloat].

How do I do that, is that possible?


Solution

  • It is unsafe to output pointers like that. As mentioned in comments you should use withUnsafeBufferPointer method to access the underlying buffer:

    let points = [
      CGPoint(x:1.2, y:3.33), 
      CGPoint(x:1.5, y:1.21), 
      CGPoint(x:1.48, y:3.97)
    ]
    
    let xValues = points.withUnsafeBufferPointer { buffer in
      return buffer.map { $0.x }
    }
    

    If you need a pointer to the array of CGFloat just use the same method as above:

    xValues.withUnsafeBufferPointer { buffer in 
      // Do things with UnsafeBufferPointer<CGFloat>
    }
    

    A good Swift Pointer tutorial here.


    Edit

    Here is a working example:

    let points = [
        CGPoint(x:1.2, y:3.33), 
        CGPoint(x:1.5, y:1.21),
        CGPoint(x:1.48, y:3.97)
    ]
    
    // Create, init and defer dealoc
    let ptr = UnsafeMutablePointer<CGFloat>.allocate(capacity: points.count)
    ptr.initialize(repeating: 0.0, count: points.count)
    defer {
        ptr.deallocate()
    }
    
    // Populate pointer
    points.withUnsafeBufferPointer { buffer in
        for i in 0..<buffer.count {
            ptr.advanced(by: i).pointee = buffer[i].x
        }
    }
    
    // Do things with UnsafeMutablePointer<CGFloat>, for instance:
    let buffer = UnsafeBufferPointer(start: ptr, count: points.count)
    
    for (index, value) in buffer.enumerated() {
        print("index: \(index), value: \(value)")
    }