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

ric*_*ter 8 objective-c swift

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

+ (NSDate *)date;
+ (NSURL *)urlWithString:(NSString *)string;
+ (instancetype)layerWithSession:(AVCaptureSession *)session
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

@interface MyManager: NSObject
+ (instancetype)manager;
@end
Run Code Online (Sandbox Code Playgroud)

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

let manager = MyManager.manager() 
// error: 'manager()' is unavailable, use object construction 'MyManager()'
Run Code Online (Sandbox Code Playgroud)

是什么赋予了?我该如何解决这些错误?

ric*_*ter 13

Swift取消了Objective-C中的alloc/ initinitialization模式,并用类似于Java或C++中的构造语法替换它.

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

class Thing {
    init(number: Int)
}
Run Code Online (Sandbox Code Playgroud)

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

let thingTwo = Thing(number: 2)
Run Code Online (Sandbox Code Playgroud)

如果它是一个没关系class或者struct,或者如果你看到其他的东西像conveniencerequiredpublic之前init...这是你如何使用它.(当然,替换Thing为您正在使用的类型,使用number您想要的实际初始2值设定项中的任何参数标签,并使用该参数的适当值.)


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

问题的所有例子:

+ (NSDate *)date;
+ (NSURL *)urlWithString:(NSString *)string;
+ (instancetype)layerWithSession:(AVCaptureSession *)session;
Run Code Online (Sandbox Code Playgroud)

......是这样的便利构造者.这意味着它们导入到斯威夫特不是类的方法(NSDate.date(),NSURL.urlWithString()等),但作为初始化:

class NSDate {
    init()
}
class NSURL {
    init?(string: String)
}
class AVCaptureVideoPreviewLayer {
    init!(session: AVCaptureSession)
}
Run Code Online (Sandbox Code Playgroud)

...并使用对象构造语法调用它们:

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

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


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

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

@interface PDQMediaManager: NSObject
+ (PDQMediaManager *)manager;
@end
Run Code Online (Sandbox Code Playgroud)

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

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

+ (PDQMediaManager *)defaultManager;
Run Code Online (Sandbox Code Playgroud)

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

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

+ (PDQMediaManager *)manager NS_SWIFT_NAME(defaultManager());
Run Code Online (Sandbox Code Playgroud)

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