如何使用 Swift 中的协议从 AnyClass 获取共享实例?

How can you get the shared instance from AnyClass using a protocol in Swift?

过去我们使用 Objective-C 以这种方式匿名获取 class 的 sharedInstance:

+ (nullable NSObject *)sharedInstanceForClass:(nonnull Class)aClass
{
    // sharedPropertyProvider
    NSObject<KVASharedPropertyProvider> *sharedPropertyProvider = [aClass conformsToProtocol:@protocol(KVASharedPropertyProvider)]
        ? (NSObject<KVASharedPropertyProvider> *)aClass
        : nil;
    if (sharedPropertyProvider == nil)
    {
        return nil;
    }
    
    // return
    return [sharedPropertyProvider.class sharedInstance];
}

它是基于协议的。我们将此协议放在每个 class 我们需要执行此操作的共享实例上。

@objc (KVASharedPropertyProvider)
public protocol KVASharedPropertyProvider: AnyObject
{
    @objc (sharedInstance)
    static var sharedInstance: AnyObject { get }
}

以上在 Objective-C 中运行良好(以及从 Swift 调用时)。然而,当试图在 Swift 中编写相同的等效代码时,似乎没有办法做到这一点。如果您使用 Objective-C 代码的这一特定行:

NSObject<KVASharedPropertyProvider> *sharedPropertyProvider = [aClass conformsToProtocol:@protocol(KVASharedPropertyProvider)]
    ? (NSObject<KVASharedPropertyProvider> *)aClass
    : nil;

并尝试将其转换为 Swift 的这一行:

let sharedPropertyProvider = aClass as? KVASharedPropertyProvider

...最初它似乎成功了。编译器只是警告您未使用 sharedPropertyProvider。但是一旦你尝试像这样使用它:

let sharedInstance = sharedPropertyProvider?.sharedInstance

它会在您进行强制转换的前一行向您发出编译器警告:

Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type 'KVASharedPropertyProvider' always fails

有什么想法吗? Swift 是否根本无法像在 Objective-C 中那样将 AnyClass 转换为协议?

如果你想知道为什么我们需要这样做,那是因为我们有多个xcframeworks需要独立运行,而一个xcframework(一个核心模块)需要选择性地获取更高层框架的共享实例提供特殊处理(如果存在)(即如果已安装),但该处理必须从较低级别启动。

编辑:

有人问 Swift 中的这段代码是什么样的(这是行不通的)。它看起来像这样:

static func shared(forClass aClass: AnyClass) -> AnyObject?
{
    guard let sharedPropertyProvider = aClass as? KVASharedPropertyProvider else
    {
        return nil
    }
    
    return type(of: sharedPropertyProvider).sharedInstance
}

以上生成警告:

Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type 'KVASharedPropertyProvider' always fails

有人建议我可能需要使用 KVASharedPropertyProvider.Protocol。看起来像这样:

static func shared(forClass aClass: AnyClass) -> AnyObject?
{
    guard let sharedPropertyProvider = aClass as? KVASharedPropertyProvider.Protocol else
    {
        return nil
    }
    
    return type(of: sharedPropertyProvider).sharedInstance
}

这会生成警告:

Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type 'KVASharedPropertyProvider.Protocol' always fails

所以,我假设你有这样的东西

protocol SharedProvider {
    static var shared: AnyObject { get }
}

class MySharedProvider: SharedProvider {
    static var shared: AnyObject = MySharedProvider()
}

如果你想使用AnyObject/AnyClass

func sharedInstanceForClass(_ aClass: AnyClass) -> AnyObject? {
    return (aClass as? SharedProvider.Type)?.shared
}

更好的方法

func sharedInstanceForClass<T: SharedProvider>(_ aClass: T.Type) -> AnyObject {
    return T.shared
}