Objective-C类中的Swift协议

44

我用Swift编写了SearcherProtocol并需要实现一个Objective-C类FileSearcher来使用该协议。

所以我尝试了这个:

#import <Foundation/Foundation.h>

@interface FileSearcher : NSObject <SearcherProtocol>

// ... class content

@end
编译器告诉我:

无法找到“SearcherProtocol”的协议声明

对应的桥接头文件(modulename-Swift.h)正在被导入到FileSearcher.m中。

SearcherProtocol导入到FileSearcher.h中会引发另一个编译器错误:module name-swift.h file not found

有人知道我做错了什么吗?

我正在使用Xcode 6 Beta 5。

编辑

以下是Swift中的协议声明:

@objc protocol SearcherProtocol
{    
    var searchNotificationTarget: SearchCompletedProtocol? { get }
    var lastSearchResults: [AnyObject] { get set }

    func search(searchParam: String, error: NSErrorPointer) -> Bool
}

搜索完成协议(SearchCompletedProtocol):

@objc protocol SearchCompletedProtocol
{
    func searchCompletedNotification(sender: AnyObject!)
}

2
协议是否使用@objc标志声明? - Sulthan
是的,没错。查看生成的头文件告诉我它在那里。 - Alex
1
你能展示一下协议声明吗? - Sulthan
当然,我明天会发布它(我现在不在办公室)。 - Alex
如果有人发现他们的Swift Delegate协议找不到,并且他们同时使用Objective-C和Swift,那是因为你缺少@objc标志。 - Will
7个回答

36

出现这种情况通常有两个原因:

  1. 模块名称错误,请查看我的答案。
  2. 存在循环引用,请参见mitrenegades下面的答案。

1.获取正确的模块名称:

如果Swift协议和Objective C都在同一个项目中,那么根据Apple的说法,您只需确保获得正确的模块名称。

对于Xcode6 beta 5,您可以在BuildSettings-> Packaging-> Product Module Name下找到它。

一个常见的错误是认为每个Swift文件/类都有自己的文件,但实际上它们都放在一个大文件中,该文件名与项目名称相同。

进一步的错误是如果模块名称包含空格,则应将其替换为下划线。

编辑:

使用您的协议,我创建了一个名为“Test”的测试项目,可以完美编译,其中包括以下文件:

TestObjClass.h

#import <Foundation/Foundation.h>
#import "Test-Swift.h"

@interface TestObjCClass : NSObject <SearcherProtocol>

@end

TestObjClass.m

#import "TestObjCClass.h"


@implementation TestObjCClass

@end

测试协议.swift

import Foundation

@objc protocol SearcherProtocol
{
    var searchNotificationTarget: SearchCompletedProtocol? { get }
    var lastSearchResults: [AnyObject] { get set }

    func search(searchParam: String, error: NSErrorPointer) -> Bool
}

@objc protocol SearchCompletedProtocol
{
    func searchCompletedNotification(sender: AnyObject!)
}

2. 避免循环引用:

Mitrenegades的回答解释了这一点,但如果你的项目需要使用显式的objc类来使用swift协议(而不仅仅是使用协议),那么就会出现循环依赖问题。原因是swift协议被定义为swift-objc头文件,然后到你的obj-c类定义,再返回到swift-objc头文件。

Mitrenegades的解决方案是使用一个Objective-C协议,这是一种方法,但如果你想要一个Swift协议,另一种方法就是重构代码,不直接使用Objective-C类,而是使用协议(例如基于某些协议的工厂模式)。任何一种方法都可能适合您的目的。


模块名称不是问题。我能够将其导入到Objective-C源代码文件中。CMD + 单击导入的文件也可以(显示Xcode为Swift代码生成的代码 - 在那里我可以看到两个协议)。 - Alex
我已经测试了这些协议,很不幸对你来说,它们对我来说运行良好。你想要项目的复制品吗?我也会更新我的回答... - James Alvarez
你说你能看到Xcode生成的Swift代码,这表明你的协议中没有小错误,这显然可能会引起问题... 你能发布完整的文件吗? - James Alvarez
我在另一个项目中也尝试了这个,编译没有问题。但是:尝试创建SearcherProtocol的实例,例如let a: SearcherProtocol = TestObjClass()会抛出一个错误,指出TestObjClass不符合协议SearcherProtocol - Alex
在.h文件中导入Swift头文件是完全正常的事情 - 除非存在循环引用,否则不会出现编译错误。我已经编辑了答案以澄清这一点。 - James Alvarez
显示剩余4条评论

28
当你拥有

#import "moduleName-Swift.h" 

如果您在想要成为委托的.h文件中,同时也将该.h文件包含在桥接头文件中,会导致循环引用,从而导致moduleName-Swift.h编译失败。对于@james_alvarez的测试项目,它可能正常工作,因为您不需要将TestObjClass.h包含到桥接头文件中。

对于我来说,在需要成为由Swift编写的类的委托的objc文件上合并的最佳方法,但是还需要将其包含在桥接头文件中,以便其他Swift文件可以访问此objc类,是创建一个单独的协议文件:

MyProtocol.h:

@protocol MyDelegate <NSObject>

-(void)didDoThis;
-(void)didDoThat;

@end

ViewController.h:

#import "MyProtocol.h"

@interface ViewController : UIViewController <MyDelegate>

MyProject-Bridging-Header.h

#import "MyProtocol.h"
#import "ViewController.h"

1
非常好的答案。我遇到了同样的问题 - 在.h文件中导入Swift头文件时找不到,最终我发现我有循环引用(虽然不是直接的),并在单独的objc文件中定义了协议。谢谢! - Tamir
我特别喜欢这个解决方案将协议与Obj C和Swift类分开,强调它的中间人地位。 - HenryRootTwo
这实际上是正确的答案。@james_alvarez的答案并不完全可靠。 - Shane D
应该是被接受的答案。 - vib

23

我知道这是很久以前的事情了,但当我给我的 Swift 代码添加协议时也遇到了同样的问题。我的协议没有被添加到“-Swift.h”头文件中,因此出现了“找不到协议声明”的错误。

问题在于我的协议没有标记为Public。我将我的协议从以下内容更改为:

@objc protocol MyProtocol { //etc.... }

变成这样:

@objc public protocol MyProtocol { //etc.... }

我仍然不完全确定为什么我需要“Public”,但似乎没有其他人需要,但嘿,它起作用了...


1
另一个有趣的观察是:似乎只有协议需要标记@objc注释。即使没有public修饰符,该类也会自动添加。 - Meriw

2
请确保在ObjectiveC文件中包含自动生成的Swift头文件。它的名称与您的项目模块名称相同,后面跟着-Swift.h
例如,如果您的项目模块是MyTarget,则应使用以下代码: #import "MyTarget-Swift.h" 如果您在Objective C文件中输入导入语句,则不会自动完成。您可以在键入导入语句后单击头文件以验证是否正确。

0

关于编程的内容,翻译如下:

你可以从 Swift 方面进行一致性部分的处理

所以你有一个 Swift 的 protocol 并想让一个 Objective-C 类型符合它,

Swift 方面

在你的协议中添加 @objc 以使其对 Objective-C 世界可见。

@objc protocol IndianCooking {
  func cookChicken()
}

Objective-C 部分

在实现的 .m 文件中,您需要执行以下操作:

#import "YourProject-Swift.h"
@interface Cheef ()<IndianCooking> { 
}

并在头文件.h中添加方法cookChicken()


-2

在 .h 文件中像这样导入委托

@protocol AnalyticProtocol;

并将其添加到 .swift 文件中

@objc public protocol AnalyticProtocol {

}

-3

尝试在您的Product_Module_Name-Prefix.pch文件中添加#import "Product_Module_Name-Swift.h"。 这对我有用,而且您现在可以从任何objc文件访问您的swift文件。


2
这是一个非常糟糕的建议。很有可能会创建循环导入。仅仅为了共享所有头文件而使用预编译头文件是对预编译头文件的滥用。现在只需忽略预编译头文件,不要在其中放置任何内容。 - Sulthan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接