Bidirectional data exchange between macOS Bundles (swift)

只谈情不闲聊 提交于 2020-06-16 17:28:29

问题


How can I pass data between macOS Bundles?

eg: MyApp.app sends data to a function or class initialiser in MyBundle.bundle, which performs its own logic on it. It could even return a value, which MyApp.app can then process further.

Fo example, a function in MyBundle (not nested in any other declaration):

    void initialise(unsigned int frameCount) {…} // not a class initialiser btw

I have tried:

  • Declaring a class in MyBundle and then loading it in MyApp using Foundation.Bundle:

    let class = (Bundle(url: url)!.classNamed("MyClass")! as! NSObject.Type).init()
    // class is an instance of NSObject, but I can override the `init()` method 
    // (in MyBundle) to do whatever. I have not found a way to implement an 
    // initialiser with parameters, and it still be recognised here.
    
  • Declaring a function in MyBundle and using CFBundleGetFunctionPointerForName to retrieve it.

    var bundles = CFBundleCreateBundlesFromDirectory(kCFAllocatorDefault, bundleDirectory, bundleExtension)! as Array
    var bundle1 = bundles.first! as! CFBundle
    
    var untypedFunctionPointer = CFBundleGetFunctionPointerForName(bundle, "initialise" as CFString)!
    
    let imp = unsafeBitCast(untypedFunctionPointer, to: IMP.self)
    
    let function = unsafeBitCast(imp, to: (@convention(c)(CUnsignedInt) -> Void).self)
    // If I specify parameters here, pass them in below, then try to use them in my objective c function, I get exc_bad_access.
    
    function(CUnsignedInt(1.0)) // Will work only when not trying to access parameters in the original objective c function.
    
    
    
    // Whatever @convention(c) function signature I use, the parameters are unusable in Objective C.
    

The critical issue with both is that I am unable to pass data in as parameters. So a print statement will work but no ability to use arguments.

Edit: Accepted answer shows the correct method for calling function pointers. The crashes I talked about are a result of using types which are not being bridged to the C family of languages properly, so stick to NSNumber, NSString etc. from the Foundation library when in doubt.


回答1:


Based on your question Call Swift function from Bundle and comments in https://stackoverflow.com/a/56281093/5329717 I presume you're after dynamically invoking in Swift a function pointer or an objective-c method with UnsafeBufferPointer argument.

First of all UnsafeBufferPointer will make a copy so for interoping I recommend switching to OpaquePointer instead.

Assuming function to pointer case with signature void cFunction(void* arg):

let fakeIMP = unsafeBitCast(functionToPointerAddress, to: IMP.self)
unsafeBitCast(fakeIMP,to:(@convention(c)(OpaquePointer)->Void).self)(bufferArg)

Assuming static objective-c method:

let receiverClass = NSClassFromString("SomeClass")
let selector: Selector = NSSelectorFromString("selectorArg:")
let methodIMP: IMP! = method_getImplementation(class_getClassMethod(receiverClass, selector))
unsafeBitCast(methodIMP,to:(@convention(c)(AnyClass?,Selector,OpaquePointer)->Any).self)(receiverClass,selector, bufferArg)

For your provided C signature

void initialise(unsigned int frameCount);

Try this:

let imp = unsafeBitCast(functionToPointerAddress, to: IMP.self)
unsafeBitCast(imp,to:(@convention(c)(Int)->Void).self)(1)


来源:https://stackoverflow.com/questions/62108482/bidirectional-data-exchange-between-macos-bundles-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!