如何使用 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
}
过去我们使用 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
}