如何在Swift 3中将'void*'从C函数转换为'UnsafeMutableRawPointer'?

Wey*_* Ng 6 c macos ios swift3 xcode8

我正在尝试将一个lua桥从Swift 2转换为Swift 3.我不是原作者,因此我不太了解库的各个方面,原作者似乎对继续处理该项目不感兴趣.我完成了大部分的转换,但仍有一个地方我被困住了,无法弄明白.我试过在SO和互联网上搜索,但找不到任何可以帮我解决问题的东西.

如果有人有兴趣查看完整的源代码,这里是我在github上的项目分支:https://github.com/weyhan/lua4swift(我的更改是在Swift3分支中)

请允许我设置上下文中我遇到的错误.有一个Userdata类,特别是在方法中userdataPointer<T>() -> UnsafeMutablePointer<T>,c函数lua_touserdata返回userdata的块地址作为void *指针类型.

用Swift 2编写的原始代码:

public class Userdata: StoredValue {

    public func userdataPointer<T>() -> UnsafeMutablePointer<T> {
        push(vm)
        let ptr = lua_touserdata(vm.vm, -1)
        vm.pop()
        return UnsafeMutablePointer<T>(ptr)
    }

    public func toCustomType<T: CustomTypeInstance>() -> T {
        return userdataPointer().memory
    }

    public func toAny() -> Any {
        return userdataPointer().memory
    }

    override public func kind() -> Kind { return .Userdata }

}
Run Code Online (Sandbox Code Playgroud)

在使用Xcode 8迁移工具进行转换后,Xcode抱怨返回行有错误Cannot invoke initializer for type 'UnsafeMutablePointer<T>' with an argument list of type '(UnsafeMutableRawPointer?)':

return UnsafeMutablePointer<T>(ptr)
Run Code Online (Sandbox Code Playgroud)

我修好了:

return (ptr?.assumingMemoryBound(to: T.self))!
Run Code Online (Sandbox Code Playgroud)

在上面的更改之后,现在Xcode 8正在抱怨调用语句createCustomType:

public func createCustomType<T: CustomTypeInstance>(setup: (CustomType<T>) -> Void) -> CustomType<T> {
    lua_createtable(vm, 0, 0)
    let lib = CustomType<T>(self)
    pop()

    setup(lib)

    registry[T.luaTypeName()] = lib
    lib.becomeMetatableFor(lib)
    lib["__index"] = lib
    lib["__name"] = T.luaTypeName()

    let gc = lib.gc
    lib["__gc"] = createFunction([CustomType<T>.arg]) { args in
        let ud = args.userdata

        // ******* Here's the line that is causing problem in Swift 3
        (ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy()
        // *******

        let o: T = ud.toCustomType()
        gc?(o)
        return .Nothing
    }

    if let eq = lib.eq {
        lib["__eq"] = createFunction([CustomType<T>.arg, CustomType<T>.arg]) { args in
            let a: T = args.customType()
            let b: T = args.customType()
            return .Value(eq(a, b))
        }
    }
    return lib
}
Run Code Online (Sandbox Code Playgroud)

我遇到困难的地方是:

(ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy()
Run Code Online (Sandbox Code Playgroud)

我相信原作者试图清除userdataPointer()调用返回的指针指向的内存块.

使用Xcode 8自动迁移工具,上面的行转换如下:

(ud.userdataPointer() as UnsafeMutableRawPointer).deinitialize()
Run Code Online (Sandbox Code Playgroud)

然而Xcode现在抱怨了Cannot convert call result type 'UnsafeMutablePointer<_>' to expected type 'UnsafeMutableRawPointer'.

从我的研究来看,对返回线的改变userdataPointer似乎是正确的,所以我认为问题在于对非安全可变的RawPointer进行转换.我已经尝试将强制转换为直接UnsafeMutableRawPointer调用ud.userdataPointer().deinitialize()但是我得到了这个错误Generic parameter 'T' could not be inferred.

我尝试过的其他方法是将UnsafeMutablePointer转换为UnsafeMutableRawPointer但它总是导致Xcode 8抱怨一件事或另一件事.有关如何使其工作的任何建议?

Ana*_*i P 3

您可能已经发现,Swift 3 尝试在指针方面提供更好的类型安全性。 UnsafeMutablePointer现在只能表示指向已知类型实例的指针。在 Swift 2 中,Cvoid *由 表示UnsafeMutablePointer<Void>,允许以相同的方式处理 void 和非 void 指针,包括尝试调用指向类型的反初始化器,这就是有destroy()问题的行中的方法代码的作用是:

  (ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy()
Run Code Online (Sandbox Code Playgroud)

在 Swift 3 中,指针对象上的反初始化器是使用结构体deinitialize()的方法调用的UnsafeMutablePointer。看来迁移助理很困惑。线路

(ud.userdataPointer() as UnsafeMutableRawPointer).deinitialize()
Run Code Online (Sandbox Code Playgroud)

没有什么意义,因为 (1)UnsafeMutablePointer不能使用asto进行转换UnsafeMutableRawPointer;(2)UnsafeMutableRawPointer没有deinitialize()方法。在 Swift 3 中,UnsafeMutableRawPointer是表示 的特殊类型void*。迁移工具犯这个错误的原因其实是可以理解的:它盲目地用 和 替换了destroy()相应deinitialize()UnsafeMutablePointer<Void>Swift 3 type UnsafeMutableRawPointer,而没有意识到转换不起作用。

我不太明白为什么调用destroy()void 指针可以在 Swift 2 中工作。也许这是程序中的一个错误,或者某些编译器技巧允许调用正确的反初始化程序。在对代码不够了解的情况下,我不能更具体地建议分析它以找出被destroy()调用的指针所指向的类型。例如,如果我们确定它始终是T以下行中使用的占位符类型:

   let o: T = ud.toCustomType()
Run Code Online (Sandbox Code Playgroud)

那么有问题的行就变成了

  (ud.userdataPointer() as UnsafeMutablePointer<T>).deinitialize()
Run Code Online (Sandbox Code Playgroud)

我们需要括号中的转换以允许编译器推断泛型参数。

感谢您提出一个有趣的问题。顺便说一句,一旦克服了这个障碍,你很可能会遇到其他问题。值得注意的一件事是Swift 3 中UnsafeMutablePointer没有这一点;.memory你必须使用.pointee它。

这是一个更新。在 Linux 上使用 Swift 2.2 后,我意识到调用强制类型destroy()转换UnsafeMutablePointer<A>asUnsafeMutablePointer<Void>不会调用 A 的析构器,即使它有一个。所以,你必须小心这条线......