cswiftunsafemutablepointer

Swift Mutable Pointer being overridden


So I have some code for doing depth detection on 2 images. The depth detection is happening in native C code. In order to make the call to the C library I have another function to build arrays of Int16 that I pass in as the parameters.

When I make the function calls directly in the parameters of the C call however, it overwrites the first function call and passes in the same parameter twice.

Here is what I mean, this is my call to the C library:

let result = ImageProcessing.depthDetection(leftPhoto.cgImage!,
                                            right: rightPhoto.cgImage!,
                                            leftFace: getFaceDetails(image: leftPhoto, face: leftFace!).mutablePointer,
                                            rightFace: getFaceDetails(image: rightPhoto, face: rightFace!).mutablePointer))

The get face details call looks like this:

private func getFaceDetails(image: UIImage, face: VisionFace) -> [Int16] {
    var details = [Int16](repeating: 0, count: 10)
    // get image size
    details[0] = Int16(image.size.width)
    details[1] = Int16(image.size.height)

    // get face bounds
    details[2] = Int16(face.frame.origin.x)
    details[3] = Int16(face.frame.origin.y)
    details[4] = Int16(face.frame.origin.x + face.frame.width)
    details[5] = Int16(face.frame.origin.y + face.frame.height)

    // get eye locations
    details[6] = Int16(truncating: face.landmark(ofType: .leftEye)?.position.x ?? 0)
    details[7] = Int16(truncating: face.landmark(ofType: .leftEye)?.position.y ?? 0)
    details[8] = Int16(truncating: face.landmark(ofType: .rightEye)?.position.x ?? 0)
    details[9] = Int16(truncating: face.landmark(ofType: .rightEye)?.position.y ?? 0)

    print("Details: \(details)")

    return details
}

and I'm getting the mutable pointer with this extension:

extension Array where Element == Int16 {
    var mutablePointer: UnsafeMutablePointer<Int16> {
        get {
            return UnsafeMutablePointer<Int16>(mutating: self)
        }
    }
}

So when I run the above ImageProcessing.depthDetection call, I can see printed out that my leftFace and rightFace arrays are indeed different and look like this:

Details: [3088, 2320, 1119, 431, 2230, 1542, 1493, 888, 1892, 882]
Details: [3088, 2320, 864, 446, 1975, 1556, 1207, 900, 1626, 890]

but when i print them out in C, they are both the same the rightFace array and look like this (I'm formatting my C logs differently but you can tell that both left and right have the same data):

0: Left (3088, 2320), Right (3088, 2320)
1: Left (864, 446), Right (864, 446)
2: Left (1975, 1556), Right (1975, 1556)
3: Left (1207, 900), Right (1207, 900)
4: Left (1626, 890), Right (1626, 890)

So how come the first getFaceDetails output is being overridden by the second one?

The strangest part of it all is that if i assign the result of getFaceDetails to a variable and then pass in that variable as the parameter like I'm doing here:

let lf = getFaceDetails(image: leftPhoto, face: leftFace!)
let rf = getFaceDetails(image: rightPhoto, face: rightFace!)

let result = ImageProcessing.depthDetection(leftPhoto.cgImage!,
                                            right: rightPhoto.cgImage!,
                                            leftFace: lf.mutablePointer,
                                            rightFace: rf.mutablePointer)

Then all of a sudden it works! My print statements in C show different data for left and right face. It's only when I pass the function calls in the parameters directly that it has the wrong data.

So whats going on here? Why doesn't the first way im doing it yield the same results as the latter?


Solution

  • As several of the comment have pointed out, this is undefined behavior:

    extension Array where Element == Int16 {
        var mutablePointer: UnsafeMutablePointer<Int16> {
            get {
                return UnsafeMutablePointer<Int16>(mutating: self)
            }
        }
    }
    

    As soon as UnsafeMutablePointer.init(mutating:) returns, the pointer it provides is meaningless. What you mean is something along these lines:

    let result = lf.withUnsafeMutableBytes { lfBytes in
        rf.withUnsafeMutableBytes { rfBytes in
            return ImageProcessing.depthDetection(leftPhoto.cgImage!,
                                                  right: rightPhoto.cgImage!,
                                                  leftFace: lfBytes,
                                                  rightFace: rhBytes)
        }
    }
    

    Alternately, you might be able to do something along these lines:

    let result = ImageProcessing.depthDetection(leftPhoto.cgImage!,
                                                right: rightPhoto.cgImage!,
                                                leftFace: &lf,
                                                rightFace: &rf)
    

    But the key point is that returning an UnsafeMutablePointer is meaningless unless you have some way to ensure the lifetime of the specific thing you've taken a pointer of, which is almost never true, except within a withUnsafe... block.