Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift Bundle.className(className:String)失败,但NSClassFromString()正常工作_Swift_Plugins_Reflection_Frameworks_Bundle - Fatal编程技术网

Swift Bundle.className(className:String)失败,但NSClassFromString()正常工作

Swift Bundle.className(className:String)失败,但NSClassFromString()正常工作,swift,plugins,reflection,frameworks,bundle,Swift,Plugins,Reflection,Frameworks,Bundle,我正在研究从Swift中的主机应用程序加载插件的概念验证。我能够在bundle类、NSClassFromString和bundle上加载框架或bundle。principalClass似乎工作正常,但我无法从bundle.classNamed函数中获得任何结果 这是我正在使用的一段代码: 用于捆绑包中的全名{ 打印(“加载框架:\(全名)”) 如果let bundle=bundle(路径:fullName),bundle.load(), 让name=fullName.split(分隔符:“.”)

我正在研究从Swift中的主机应用程序加载插件的概念验证。我能够在bundle类、
NSClassFromString
bundle上加载框架或bundle。principalClass
似乎工作正常,但我无法从
bundle.classNamed
函数中获得任何结果

这是我正在使用的一段代码:

用于捆绑包中的全名{
打印(“加载框架:\(全名)”)
如果let bundle=bundle(路径:fullName),bundle.load(),
让name=fullName.split(分隔符:“.”)。首先{
将typename=bundle.classNamed(name+“.Plugin”)设为?NSObject.Type
将typeNS=NSClassFromString(name+“.Plugin”)设为?NSObject.Type
让typeNamedV2=bundle.classNamed(name+“.PluginV2”)作为?NSObject.Type
将typeNSV2=NSClassFromString(名称+“.PluginV2”)设为?NSObject.Type
让typePrincipal=bundle.principalClass作为?NSObject.Type
打印(“From bundle.classNamed:\(initPlugin(From:typeNamed)?.description???”)
打印(“来自NSClassFromString:\(initPlugin(来自:typeNS)?.description???”)
打印(“来自bundle.classNamed:\(initPlugin(来自:typeNamedV2)?.description??”)
打印(“来自NSClassFromString:\(initPlugin(来自:typeNSV2)?.description???”)
打印(“来自bundle.principalClass:\(initPlugin(来自:typePrincipal)?.description??”)
bundle.unload()
}
}
以下是输出:

Loading framework: One.framework
From bundle.classNamed: 
From NSClassFromString: <One.Plugin: 0x102802060>
From bundle.classNamed: 
From NSClassFromString: <One.PluginV2: 0x102802060>
From bundle.principalClass: <One.Plugin: 0x102801650>
Loading framework: CommonInterface.framework
From bundle.classNamed: 
From NSClassFromString: 
From bundle.classNamed: 
From NSClassFromString: 
From bundle.principalClass: 
Loading framework: Bundle.bundle
From bundle.classNamed: 
From NSClassFromString: <Bundle.Plugin: 0x102b08040>
From bundle.classNamed: 
From NSClassFromString: <Bundle.PluginV2: 0x102b08f80>
From bundle.principalClass: <Bundle.Plugin: 0x102801ea0>
加载框架:1.framework
从bundle.classNamed:
从NSClassFromString:
从bundle.classNamed:
从NSClassFromString:
从bundle.principalClass:
加载框架:CommonInterface.framework
从bundle.classNamed:
从NSClassFromString:
从bundle.classNamed:
从NSClassFromString:
从bundle.principalClass:
加载框架:Bundle.Bundle
从bundle.classNamed:
从NSClassFromString:
从bundle.classNamed:
从NSClassFromString:
从bundle.principalClass:
项目配置为Swift 5和Xcode 11大楼

在这里您可以找到POC的完整源代码:


欢迎任何提示/反馈

如果通过
bundle(路径:)
和相对路径加载了bundle,则
bundle.classNamed()
似乎不起作用。下面是一个最小的自包含示例,演示了该问题:

// This works:

let b1 = Bundle(path: "/System/Library/Frameworks/Foundation.framework")!
print(b1.load()) // true
let c1: AnyClass? = b1.classNamed("NSString")
print(c1 as Any) // Optional(NSString)

// This does not work

FileManager.default.changeCurrentDirectoryPath("/System/Library/Frameworks")
let b2 = Bundle(path: "Foundation.framework")!
print(b2.load()) // true
let c2: AnyClass? = b2.classNamed("NSString")
print(c2 as Any) // nil
那对我来说像个虫子。在您的情况下,快速修复方法是通过绝对路径加载捆绑包:

if let bundle = Bundle(path: "\(path)/\(fullName)")
或者使用URL,以下是必要的修改:

// main.swift:

let pluginHost = PluginHost()
pluginHost.loadPlugins(at: Bundle.main.bundleURL)

// PluginHost.swift:

func loadPlugins(at url: URL) {
    let fileManager = FileManager.default

    let bundles = (try? fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
        .filter { $0.pathExtension == "bundle"
            || $0.pathExtension == "framework"
        }) ?? []

    for bundleURL in bundles {
        print("Loading framework: \(bundleURL)")
        if let bundle = Bundle(url: bundleURL) {
            let bundleName = bundleURL.deletingPathExtension().lastPathComponent
            print("Name:", bundleName)

            let typeNamed = bundle.classNamed(bundleName + ".Plugin") as? NSObject.Type
            let typeNamedV2 = bundle.classNamed(bundleName + ".PluginV2") as? NSObject.Type

            print("From bundle.classNamed: \(initPlugin(from: typeNamed)?.description ?? "")" )
            print("From bundle.classNamed V2: \(initPlugin(from: typeNamedV2)?.description ?? "")" )

        }
    }
}
输出:

Start plugin loading
Loading framework: file:///.../One.framework/
Name: One
From bundle.classNamed: <One.Plugin: 0x100537f90>
From bundle.classNamed V2: <One.PluginV2: 0x1005388a0>
Loading framework: file:///.../CommonInterface.framework/
Name: CommonInterface
From bundle.classNamed: 
From bundle.classNamed V2: 
Loading framework: file:///.../Bundle.bundle/
Name: Bundle
From bundle.classNamed: <Bundle.Plugin: 0x10053a1d0>
From bundle.classNamed V2: <Bundle.PluginV2: 0x10053a1d0>
End plugin loading
Program ended with exit code: 0

请注意,如果某个包的任何加载类仍在使用中,则不能卸载该包,否则会导致程序崩溃。请注意,谢谢!这只是一次尝试,想得到bundle.classnamedworkingthankyou的详细答案!非常感谢,我将相应地更新示例存储库。@lechuckcaptain:弄明白这一点很有趣–尽管我花了相当长的时间才找出实际问题!
if let cls = bundle.classNamed(bundleName + ".Plugin") as? PluginInterface.Type {
    let plugin = cls.init()
    plugin.doSomething()
}
if let cls = bundle.classNamed(bundleName + ".PluginV2") as? PluginInterface.Type {
    let plugin = cls.init()
    plugin.doSomething()
}