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

发布于 2025-01-09 03:38:58 字数 2659 浏览 2 评论 0原文

过去,我们使用 Objective-C 以这种方式匿名获取类的共享实例:

+ (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];
}

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

@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

它就会在您进行强制转换的上一行上给出编译器警告:

从“AnyClass”(又名“AnyObject.Type”)转换为不相关的类型 “KVASharedPropertyProvider”总是失败

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

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

编辑:

有人问这段代码在 Swift 中是什么样子(这不起作用)。它看起来像这样:

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

上面生成警告:

从“AnyClass”(又名“AnyObject.Type”)转换为不相关的类型 “KVASharedPropertyProvider”总是失败

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

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

这会生成警告:

从“AnyClass”(又名“AnyObject.Type”)转换为不相关的类型 “KVASharedPropertyProvider.Protocol”总是失败

In the past we've used Objective-C to anonymously get the sharedInstance of a class this way:

+ (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];
}

It's protocol based. We put this protocol on every class we have with a shared instance where we need to do this.

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

The above works fine in Objective-C (and when called from Swift). When attempting to write the same equivalent code in Swift, however, there appears to be no way to do it. If you take this specific line(s) of Objective-C code:

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

And attempt to convert it to what should be this line of Swift:

let sharedPropertyProvider = aClass as? KVASharedPropertyProvider

... initially it appears to succeed. The compiler just warns you that sharedPropertyProvider isn't be used. But as soon as you attempt to use it like so:

let sharedInstance = sharedPropertyProvider?.sharedInstance

It gives you the compiler warning back on the previous line where you did the cast:

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

Any ideas? Is Swift simply not capable of casting AnyClass to a protocol in the same way that it could be in Objective-C?

In case you're wondering why we need to do this, it's because we have multiple xcframeworks that need to operate independently, and one xcframework (a core module) needs to optionally get the shared instance of a higher level framework to provide special processing if present (i.e. if installed) but that processing must be initiated from the lower level.

Edit:

It was asked what this code looked like in Swift (which does not work). It looks like this:

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

The above generates the warning:

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

It was suggested I may need to use KVASharedPropertyProvider.Protocol. That looks like this:

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

And that generates the warning:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

信愁 2025-01-16 03:38:58

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

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
}

So, I assume you have something like this

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

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

If you want to use AnyObject/AnyClass

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

Better approach

func sharedInstanceForClass<T: SharedProvider>(_ aClass: T.Type) -> AnyObject {
    return T.shared
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文