如何在 Swift 中调用 Objective C 类方法 [英] How to call an Objective C class method in Swift

查看:72
本文介绍了如何在 Swift 中调用 Objective C 类方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 Objective-C 头文件中,我有以下类方法声明:

On my Objective-C header, I have the following class method declaration:

@interface WXMediaMessage : NSObject

    +(WXMediaMessage *) message;

我试着像下面这样调用这个方法:

I tried to call this method like the following:

var message : WXMediaMessage = WXMediaMessage.message()

但它不起作用.我已经设置了桥接头.

But it doesn't work. I have set up bridging-header.

如果我按照描述调用方法,它会显示一个错误,说'message()'不可用:使用对象构造'WXMediaMessage()'.

If I call the method as described, it will show an error says 'message()' is unavailable: use object construction 'WXMediaMessage()'.

如果我使用 WXmediaMessage(),错误就会消失.但是,如果在Objective C中调用它会返回与[WXMediaMessage message]相同的结果吗?

If I use WXmediaMessage(), error does go away. However, will it return the same result as [WXMediaMessage message] would if called in Objective C?

MYAPP_bridging_header.h

#ifndef MYAPP_bridging_header_h
#define MYAPP_bridging_header_h

#import "WXApi.h"
#import "WXApiObject.h"

#endif

WXApiObject.h 代码段

@interface WXMediaMessage : NSObject

   +(WXMediaMessage *) message;

@end

WXApiObject.m

(It is an external api, so I can't see the content)

推荐答案

我意思是:但为什么?

这就是我们要问的.Matt 的回答是正确的,因为重命名方法可以解决问题,但我们仍然要问为什么.

That's what we're left asking. Matt's answer is correct in that renaming the method will fix the problem, but we're still left asking why.

答案是因为 Swift 实际上将便捷方法重新映射到构造函数.

The answer is because Swift actually remaps convenience methods to constructors.

因此,虽然我们必须在 Objective-C 中像这样调用方法:

So while we'd have to call the method like this in Objective-C:

[WXMediaMessage message];

在 Swift 中,我们应该改为简单地调用 WXMediaMessage().在 Objective-C 中,这相当于调用:

In Swift, we should instead be simply calling WXMediaMessage(). In Objective-C, this is the equivalent to calling:

[[WXMediaMessage alloc] init];

<小时>

需要注意的是,当我们使用 Swift 默认初始化程序时,不会调用实际的工厂方法.这将直接进入 init 而不调用工厂包装器.但最佳实践建议我们的工厂方法应该只是 [[self alloc] init];.一个值得注意的建议可能是单例,但如果我们有单例工厂方法,我们应该遵循 Apple 设置的模式,并使用共享"前缀命名我们的工厂方法:


It's important to note that the actual factory method doesn't get called when we use the Swift default initializer. This will go straight to init without calling the factory wrapper. But best practice would suggest that our factory method should be nothing more than [[self alloc] init];. The one notable suggestion might be a singleton, but if we have a singleton factory method, we should follow the pattern Apple has set, and name our factory method with the "shared" prefix:

+ (instancetype)sharedMessage;

以下 Swift 代码将完全有效:

And the following Swift code would be perfectly valid:

let message = WXMediaMessage.sharedMessage()

但是如果message不是单例,那么它应该不会超过return [[self alloc] init];,这就是我们得到的使用以下 Swift 代码:

But if message isn't a singleton, then it should be no more than return [[self alloc] init];, which is what we'd get with the following Swift code:

let message = WXMediaMessage()

这就是错误信息告诉我们要做的:

And this is what the error message is telling us to do:

'message()' is unavailable: use object construction 'WXMediaMessage()'

错误消息告诉我们使用默认的 Swift 初始值设定项而不是 Objective-C 工厂方法.这是有道理的.任何人想要在 Objective-C 中使用工厂方法的唯一原因是因为 [[MyClass alloc] init] 看起来很难看.我们所有的初始化仍然应该在 init 方法中完成,但是...不是在我们创建的工厂方法中,因为我们不想查看 alloc] init]...

The error message tells us to use the default Swift initializer rather than the Objective-C factory method. And this makes sense. The only reason anyone ever wanted factory methods in Objective-C was because [[MyClass alloc] init] looks ugly. All of our initialization should still be done in the init method however... not in the factory method we create because we'd rather not look at alloc] init]...

考虑以下 Objective-C 类:

Consider the following Objective-C class:

@interface FooBar : NSObject

+ (instancetype)fooBar;

- (void)test;

@end

@implementation FooBar

- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@"initialized");
    }
    return self;
}

+ (instancetype)fooBar {
    NSLog(@"initialized with fooBar");
    return [[self alloc] init];
}

- (void)test {
    NSLog(@"Testing FooBar");
}

@end

现在使用以下 Swift 代码:

Now using the following Swift code:

let var1 = FooBar()
var1.test()

我们得到以下输出:

2014-11-08 10:48:30.980 FooBar[5539:279057] initialized
2014-11-08 10:48:30.981 FooBar[5539:279057] Testing FooBar

如我们所见,fooBar 方法从未被调用.但实际上,如果您在构建您的 Objective-C 类时考虑到了良好的实践,那么这样命名的类方法应该永远不会超过:

As we can see, the method fooBar is never called. But truly, if you're building your Objective-C classes correct with good practice in mind, a class method named as such should never be any more than:

return [[self alloc] init];

并且您的 init 方法应该处理所有设置.

And your init method should be handling all the set up.

当我们使用不那么简单的初始化器和工厂方法时,发生的事情变得更加明显.

What's happening becomes more obvious when we use less simply initializers and factory methods.

考虑我们是否添加了一种用数字初始化我们的类的方法:

Consider if we add a way to initialize our class with a number:

@interface FooBar : NSObject

+ (instancetype)fooBarWithNumber:(int)number;
- (void)test;

@end

@implementation FooBar {
    int _number;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@"initialized");
    }
    return self;
}

- (instancetype)initWithNumber:(int)number {
    self = [self init];
    if (self) {
        NSLog(@"init with number: %i", number);
        _number = number;
    }
    return self;
}

+ (instancetype)fooBarWithNumber:(int)number {
    NSLog(@"fooBar with number: %i", number);
    return [[self alloc] initWithNumber:number];
}

- (void)test {
    NSLog(@"Testing FooBar (number: %i)", _number);
}

@end

此时,请注意我们的 .h 公开了 fooBarWithNumber: 方法,而不是 initWithNumber: 方法.

At this point, note that our .h exposes the fooBarWithNumber: method, but not the initWithNumber: method.

现在让我们回到 Swift 代码:

Now let's go back to the Swift code:

let var1 = FooBar(number: 3)
var1.test()

Swift 将我们的 fooBarWithNumber: 方法变成了一个初始化器:FooBar(number:Int32)

Swift has turned our fooBarWithNumber: method into an initializer: FooBar(number:Int32)

这是输出:

2014-11-08 10:57:01.894 FooBar[5603:282864] fooBar with number: 3
2014-11-08 10:57:01.894 FooBar[5603:282864] initialized
2014-11-08 10:57:01.895 FooBar[5603:282864] init with number: 3
2014-11-08 10:57:01.895 FooBar[5603:282864] Testing FooBar (number: 3)

我们的fooBarWithNumber:方法被调用,它调用了initWithNumber:,它调用了init.

Our fooBarWithNumber: method is called, which calls initWithNumber:, which calls init.

所以为什么"这个问题的答案是因为 Swift 将我们的 Objective-C 初始化器和工厂方法转换为 Swift 风格的初始化器.

So the answer to the question, "WHY" is because Swift translates our Objective-C initializers and factory methods into Swift style initializers.

为了进一步扩展,问题甚至不仅仅在于方法名称本身.它是方法名、类名和返回类型的组合.在这里,我们使用 instancetype 作为返回类型.如果我们使用 instancetype 或我们特定的类类型,就会遇到问题.但请考虑这一点:

To expand on this further, the problem isn't even just with the method name itself. It's a combination of the method name, class name, AND return type. Here, we've used instancetype as a return type. If we use instancetype, or our specific class type, we run into the problem. But however consider this:

@interface FooBar

+ (NSArray *)fooBar;

@end

并假设该类方法的一些有效实现与类名匹配,但返回类型与我们的类不匹配.

And presuming some valid implementation of this class method which matches the class name, but the return type does NOT match our class.

现在以下方法是完美有效的 Swift 代码:

Now the following method is perfectly valid Swift code:

let fooBarArray = FooBar.fooBar()

因为返回类型与类不匹配,Swift 无法将其转换为类初始化器,所以我们的方法没问题.

Because the return type doesn't match the class, Swift can't translate this into a class initializer, so our method is fine.

还值得注意的是,如果我们选择 id 作为我们方法的返回类型,例如:

It's also worth noting that if we choose id as the return type of our method, as such:

+ (id)fooBar;

Swift 会让我们摆脱它......但它会警告我们我们的变量被推断为具有类型'AnyObject!'并建议我们添加一个显式类型,因此建议这样做:

Swift will let us get away with it... but it will warn us that our variable is inferred to have the type 'AnyObject!' and recommends we add an explicit type, so it'd recommend this:

let var1: FooBar = FooBar.fooBar()

它允许我们这样做...但最佳实践几乎肯定会建议不要使用 id 作为您的返回类型.Apple 最近刚刚将其几乎所有的 id 返回类型更改为 instancetype.

And it'd allow us to do that... but best practice almost certainly recommends against using id as your return type. Apple just recently changed almost all of its id return types over to instancetype.

这篇关于如何在 Swift 中调用 Objective C 类方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆