简体   繁体   中英

withMemoryRebound in Swift 3 is not working for an Objective-C struct pointer

I've been trying to interface to an Objective-C library that only exposed the pointer to a C struct array and the count of in that array.

I've used withMemoryRebound to the struct and set the capacity as I've seen in other posts here and elsewhere. However, the array I get in Swift has garbage data. As a workaround, I have added an accessor method to the library. But I would really prefer to not modify the library.

Here are the components of a test app that demonstrates the problem followed by the expected results:

Circle.h:

#import <Foundation/Foundation.h>

typedef struct {
    float x, y;         /**< Position in pixels. */
    float radius;       /**< Radius in pixels. */
} circle;

@interface CircleGenerator : NSObject {
}

@property (nonatomic)      size_t              numCircles;
@property (nonatomic, readonly) const circle*  circles;

- (id)init;
// Added as a workaround:
- (void)fetchCircle:(size_t) i circle:(circle *) c;

@end

Circle.m:

#import "Circle.h"

@interface CircleGenerator ()

@end

const circle myCircles[] = {
    {.x=2467.38184, .y=338.520477, .radius=140.096466},
    {.x=438.689453, .y=1879.302, .radius=116.18354}
};

@implementation CircleGenerator

- (id)init {
    _numCircles = 2;
    _circles = myCircles;
    return self;
}

// Added as a workaround:
- (void)fetchCircle:(size_t) i circle:(circle *) c {
    *c = _circles[i];
}

@end

ViewController.swift:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let circleGen = CircleGenerator()!

        var circlesPtr = UnsafePointer(circleGen.circles)
        withUnsafePointer(to: &circlesPtr) {
            $0.withMemoryRebound(to: circle.self, capacity: circleGen.numCircles, {
                for i in 0..<circleGen.numCircles {
                    print($0[i])
                }
            })
        }
        print("\nShould be:")
        var nextCircle:circle = circle(x: 0, y: 0, radius: 0)
        for i in 0..<circleGen.numCircles {
            circleGen.fetchCircle(i, circle: &nextCircle)
            print(nextCircle)
        }
    }    
}

Output:

circle(x: 6.70394638e-38, y: 1.40129846e-45, radius: 2.47778904e+18)
circle(x: 4.59163468e-41, y: 6.7002974e-38, radius: 1.40129846e-45)

Should be:

circle(x: 2467.38184, y: 338.520477, radius: 140.096466)
circle(x: 438.689453, y: 1879.302, radius: 116.18354)

Also, $0.pointee and $.pointee.x , etc. show the incorrect values of the first element.

Do I have a bug, or should I report this to Apple?

circleGen.circles is already a typed pointer ( UnsafePointer<circle> ), there is no need to use withUnsafePointer() :

for i in 0..<circleGen.numCircles {
    let circle = circleGen.circles[i]
    print(circle)
}

Output:

circle(x: 2467.38184, y: 338.520477, radius: 140.096466)
circle(x: 438.689453, y: 1879.302, radius: 116.18354)

Or create a UnsafeBufferPointer which is a "array-like" collection:

let circles = UnsafeBufferPointer(start: circleGen.circles, count: circleGen.numCircles)
for circle in circles {
    print(circle)
}

But if you are curious why your code does not work: You are rebinding the pointer to the circlesPtr variable itself, not the pointer stored in that variable. You would get the correct result with

let circlesPtr = UnsafePointer(circleGen.circles)!
circlesPtr.withMemoryRebound(to: circle.self, capacity: circleGen.numCircles, {
    for i in 0..<circleGen.numCircles {
        print($0[i])
    }
})

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