【问题标题】:Swift error: class method "someMethod() is unavailable, use object construction Class()"Swift 错误:类方法“someMethod() 不可用,使用对象构造 Class()”
【发布时间】:2016-01-25 23:51:43
【问题描述】:

Objective-C API 使用类方法进行大量对象构造:

+ (NSDate *)date;
+ (NSURL *)urlWithString:(NSString *)string;
+ (instancetype)layerWithSession:(AVCaptureSession *)session

有时我什至看到这些作为类方法出现在旧的 Swift 教程中,但是当我尝试调用它们时,我得到如下编译器错误:

date()不可用,使用对象构造NSDate()

urlWithString()不可用,使用对象构造NSURL(string:)

layerWithSession()不可用,使用对象构造AVCaptureVideoPreviewLayer(session:)

(我在文档和 Xcode 为 SDK 类生成的 Swift 接口中看到了类似 convenience init(URL url: NSURL) 的声明,但它们与这些有什么关系?)

当我在自己的 ObjC 代码中声明类方法时,甚至会发生这种情况,例如单例模式:

@interface MyManager: NSObject
+ (instancetype)manager;
@end

当我尝试从 Swift 中使用它们时,我得到了同样的错误:

let manager = MyManager.manager() 
// error: 'manager()' is unavailable, use object construction 'MyManager()'

什么给了?如何修复这些错误?

【问题讨论】:

  • 我看到很多问题都以这个问题为根本原因,但讨论和答案过于依赖特定案例而无法标记为彼此重复。这是对问题进行概括的尝试——对于有权关闭重复项的其他人,如果您认为它足够好地解决了该问题,请随意欺骗此问答。 (我自己会弃权,让社区来判断是否值得欺骗。)

标签: objective-c swift


【解决方案1】:

Swift 取消了来自 Objective-C 的 alloc/init 初始化模式,并用类似于 Java 或 C++ 中的构造语法取而代之。

在 Swift 类接口(或 Apple 的文档)中,您会看到如下内容:

class Thing {
    init(number: Int)
}

这意味着您可以使用以下“构造语法”调用Thing

let thingTwo = Thing(number: 2)

不管是class 还是struct,或者您在init 之前看到的其他内容,例如conveniencerequiredpublic...这就是您使用它的方式. (当然,将Thing 替换为您正在使用的类型,将number 替换为您想要的实际初始化程序中的任何参数标签,并将2 替换为该参数的适当值。)


当最初在 ObjC 中定义的类被导入 Swift 时,Swift 编译器会做很多事情来使这些 API 看起来和感觉更像原生 Swift 代码。其中之一是将使用类方法作为便利构造函数的常见模式替换为真正的初始值设定项。

问题中的所有示例:

+ (NSDate *)date;
+ (NSURL *)urlWithString:(NSString *)string;
+ (instancetype)layerWithSession:(AVCaptureSession *)session;

... 就是这样的便利构造函数。这意味着它们不是作为类方法(NSDate.date()NSURL.urlWithString() 等)导入 Swift,而是作为初始化器:

class NSDate {
    init()
}
class NSURL {
    init?(string: String)
}
class AVCaptureVideoPreviewLayer {
    init!(session: AVCaptureSession)
}

...你用对象构造语法来调用它们:

let date = NSDate()
let url = NSURL(string: "http://apple.com")
let layer = AVCaptureVideoPreviewLayer(session: mySession)

事实上,当您看到有关“不可用,使用对象构造”的错误消息时,Xcode 几乎总是提供自动修复——插入正确的语法。


这是如何工作的? Swift 查找类的可能“基本名称”,并在这些名称和类方法/初始化方法名称之间进行匹配。 (它还检查返回该类实例的类方法、以“init...”开头的初始化程序等)

例如,Swift 将以下内容视为便利构造函数:

@interface PDQMediaManager: NSObject
+ (PDQMediaManager *)manager;
@end

如果您(或 PDQMediaManager 的原始开发人员)打算让该类充当单例,并且 +manager 方法返回单例实例,这可能是一个问题 - 如果 Swift 开发人员写 @ 987654343@,他们将构建一个新实例而不是检索单例。

在这种情况下,一个很好的解决方案是重命名单例方法:

+ (PDQMediaManager *)defaultManager;

这不会被视为便利构造函数,因此 Swift 客户端可以调用 PDQMediaManager.defaultManager() 来获取单例实例。

您也可以使用NS_SWIFT_NAME 为 Swift 客户端重命名单例方法:

+ (PDQMediaManager *)manager NS_SWIFT_NAME(defaultManager());

如果您不使用违规方法控制代码库,最好的办法可能是编写自己的 ObjC 代码以转发到单例方法并通过桥接头将其公开给您的 Swift 代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-04
    • 1970-01-01
    • 2018-09-14
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多