objective-c - 如何在 Swift 中调用 Objective C 类方法

标签 objective-c swift

在我的 Objective-C header 上,我有以下类方法声明:

@interface WXMediaMessage : NSObject

    +(WXMediaMessage *) message;

我试着像下面这样调用这个方法:
var message : WXMediaMessage = WXMediaMessage.message()

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

编辑:

如果我按照描述调用该方法,它会显示一个错误:'message()' is unavailable: use object construction 'WXMediaMessage()' .

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

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)

最佳答案

I mean: BUT WHY?



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

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

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

在 Swift 中,我们应该简单地调用 WXMediaMessage() .在 Objective-C 中,这相当于调用:
[[WXMediaMessage alloc] init];

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

以下 Swift 代码将完全有效:
let message = WXMediaMessage.sharedMessage()

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

这就是错误消息告诉我们要做的事情:

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

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

考虑以下 Objective-C 类:
@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 代码:
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 类时考虑到了良好的实践,这样命名的类方法应该永远不会超过:
return [[self alloc] init];

还有您的 init方法应该处理所有设置。

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

考虑我们是否添加了一种用数字初始化我们的类的方法:
@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:方法。

现在让我们回到 Swift 代码:
let var1 = FooBar(number: 3)
var1.test()

swift 已经把我们的 fooBarWithNumber:方法进入初始化程序: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 .

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

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

+ (NSArray *)fooBar;

@end

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

现在以下方法是完全有效的 Swift 代码:
let fooBarArray = FooBar.fooBar()

由于返回类型与类不匹配,Swift 无法将其转换为类初始值设定项,因此我们的方法没问题。

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

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

它允许我们这样做......但最佳实践几乎肯定建议不要使用 id作为您的返回类型。苹果最近刚刚改变了几乎所有的id返回类型到 instancetype .

关于objective-c - 如何在 Swift 中调用 Objective C 类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26818432/

相关文章:

objective-c - awakeFromNib 末尾布局不正确 - 需要调整大小来纠正它

ios - 将 stackview 中的项目在水平轴上彼此靠近对齐

ios - 在 UICollectionViewCell 中圈出 ImageView

ios - 如何在后台保持 iphone ios websocket 连接?

objective-c - 如何将 NSDateComponents 存储到 NSUserDefaults 中?

ios - 在表格 View 中看不到部分

swift - 让 tvOS 游戏 handle 按钮每次按下按钮时仅发送一次消息

pointers - UnsafeMutablePointer<Int8> 来自 Swift 中的字符串

xcode - dyld : Library not loaded: @rpath/libswiftContacts. 动态库

objective-c - 如何阻止 UIWebView 播放 mp3?