在我的Objective-C标题上,我有以下类方法声明:
@interface WXMediaMessage : NSObject
+(WXMediaMessage *) message;
我试着像下面这样调用这个方法:
var message : WXMediaMessage = WXMediaMessage.message()
但它不起作用。我已经设置了bridging-header。
编辑:
如果我按照描述调用方法,则会显示错误'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)
答案 0 :(得分:30)
这就是我们要问的问题。马特的答案是正确的,重命名方法将解决问题,但我们仍然在问为什么。
答案是因为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
作为返回类型。 Apple最近刚将其所有id
种类型的返回类型更改为instancetype
。
答案 1 :(得分:10)
问题在于名称。 Swift不会导入工厂构造函数。它通过注意名称与类名末尾相同来检测这些。
将message
方法名称更改为singletonMessage
,并且一切都会正常。
如果名称为message
,您会看到:
但是,如果我将名称更改为messager
,则编译就好了。
打个比方,试试这个:
var thing : HeyHo = HeyHo.ho()
您将收到相同的错误消息。