Swift错误:类方法" someMethod()不可用,使用对象构造Class()"

时间:2016-01-25 23:51:44

标签: objective-c swift

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

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

有时候我甚至会在旧的Swift教程中看到这些作为类方法出现,但是当我尝试调用它们时,我会遇到类似这样的编译器错误:

  

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

     

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

     

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

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

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

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

当我尝试在Swift中使用它们时,我会遇到同样的错误:

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

是什么给出的?如何解决这些错误?

1 个答案:

答案 0 :(得分:13)

Swift取消了Objective-C中的alloc / init初始化模式,并将其替换为类似于Java或C ++中的构造语法。

在Swift类界面(或Apple的文档中)中,您会看到以下内容:

class Thing {
    init(number: Int)
}

这意味着您可以使用以下"构造语法"来调用它来创建Thing

let thingTwo = Thing(number: 2)

如果它是classstruct,或者您看到conveniencerequired或{{{{}等其他内容,则无关紧要1}} public之前...那是你如何使用它。 (当然,将init替换为您正在使用的类型,将Thing替换为您想要的实际初始值设定项中的任何参数标签,并将number替换为具有适当值的值那个参数。)

当最初在ObjC中定义的类被导入到Swift中时,Swift编译器会做很多事情来使这些API看起来更像本机Swift代码。其中一个方面是将类方法的常用模式替换为具有实际初始化器的便捷构造函数。

问题中的所有例子:

2

......是这样的便利构造者。这意味着他们不是作为类方法导入Swift(+ (NSDate *)date; + (NSURL *)urlWithString:(NSString *)string; + (instancetype)layerWithSession:(AVCaptureSession *)session; NSDate.date()等),而是作为初始化器:

NSURL.urlWithString()

...然后用对象构造语法调用它们:

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

事实上,当你看到关于"的错误消息不可用时,使用对象构造",Xcode几乎总是提供自动修复 - 它可以插入正确的语法。

这是如何工作的? Swift寻找可能的"基本名称"一个类,并匹配这些名称和类方法/初始化方法名称。 (它还检查返回该类实例的类方法,以&#34开头的初始化器;初始化..."等等。)

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

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

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

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

PDQMediaManager()

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

您也可以使用PDQMediaManager.defaultManager()仅为Swift客户端重命名单例方法:

NS_SWIFT_NAME

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