【问题标题】:Swift protocol in Objective-C classObjective-C 类中的 Swift 协议
【发布时间】:2014-08-12 13:54: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!)
}

【问题讨论】:

  • 协议是否使用@objc 标志声明?
  • 是的。查看生成的头文件告诉我它就在那里。
  • 你能给我们看一下协议声明吗?
  • 当然。我明天会发布(我现在不在办公室)。
  • 如果有人发现他们的 Swift Delegate 协议未找到并且他们同时使用 Objective-C 和 Swift,那是因为您缺少 @objc 标志。

标签: objective-c swift


【解决方案1】:

发生这种情况有两个常见原因:

  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

TestProtocol.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 的回答解释了这一点,但如果您的项目需要使用使用 swift 协议的显式 objc 类(而不仅仅是使用协议),那么您将遇到循环问题。原因是 swift 协议被定义为 swift-objc 标头,然后是您的 obj-c 类定义,然后又是 swift-objc 标头。

Mitrenegades 的解决方案是使用objective-c 协议,这是一种方法,但如果你想要一个swift 协议,那么另一种方法是重构代码以便不直接使用objective-c 类,而是使用协议(例如一些基于协议的工厂模式)。无论哪种方式都可能适合您的目的。

【讨论】:

  • 模块名不是问题。我可以将它导入到 Objective-C 源代码文件中。 CMD + 单击导入的文件也可以(显示 Xcode 为 Swift 代码生成的代码 - 我可以看到这两种协议)。
  • 我已经测试了协议,可能对你来说很不幸,它们对我来说很好,你想要一份项目的副本吗?我也会更新我的答案...
  • 你说你可以看到 Xcode 生成的 swift 代码,这表明你在协议中没有任何小错误,这显然会导致问题......你能发布完整的文件吗?跨度>
  • 我也在一个单独的项目中试过这个,它编译没有问题。但是:尝试像let a: SearcherProtocol = TestObjClass() 一样创建SearcherProtocol 的实例会引发TestObjClass 不符合协议SearcherProtocol 的错误。
  • 那么您需要将协议的方法/变量设置为可选,或者实际上将它们设置为 TestObjClass - 您是否设法使其在原始项目中工作?
【解决方案2】:

当你有

#import "moduleName-Swift.h" 

在您想成为委托的 .h 文件中,并且您在桥接头文件中也有该 .h 文件,有一个循环引用导致 moduleName-Swift.h 编译失败。对于@james_alvarez 的测试项目,它可能有效,因为您不需要将 TestObjClass.h 包含到桥接头中。

对我来说,组合需要成为用 swift 编写的类的委托的 objc 文件的最佳方法,但也需要包含在桥接头中,以便其他 swift 文件可以访问这个 objc 类,这是创建一个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"

【讨论】:

  • 很好的答案。我遇到了同样的问题 - 在 .h 文件中导入时找不到 Swift 头文件,最终我发现我有循环引用(虽然不是直接的),并在单独的 objc 文件中定义了协议。谢谢!
  • 我特别喜欢这个解决方案将协议从 Obj C 和 Swift 类中分离出来,强调它的中间人状态。
  • 这其实是正确的答案。 @james_alvarez 的答案并不完全证明。
  • 应该是公认的答案
【解决方案3】:

我知道这是很久以前的事了,但是当我在我的 Swift 代码中添加协议时,我遇到了同样的问题,并且它没有被添加到 -Swift.h 头文件中,因此“找不到协议声明”

问题是我的协议未标记为公共。我改变了我的协议:

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

到这里:

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

我仍然不完全确定为什么我需要“公共”,但似乎没有其他人需要,但是嘿,它有效......

【讨论】:

  • 另一个有趣的观察:似乎只有协议需要用@objc 注释标记。类会自动添加(即使没有 public 修饰符)
【解决方案4】:

确保在 ObjectiveC 文件中包含自动生成的 Swift 标头。它将与您的项目模块同名,后跟-Swift.h

例如,如果您的项目模块是 MyTarget,那么您将使用:

#import "MyTarget-Swift.h"

如果您在 Objective C 文件中输入导入,它不会自动完成。输入文件后,您可以通过命令单击标题来验证您是否拥有正确的文件。

【讨论】:

    【解决方案5】:

    您可以从 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()

    【讨论】:

      【解决方案6】:

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

      @protocol AnalyticProtocol;
      

      并将其添加到 .swift 文件中

      @objc public protocol AnalyticProtocol {
      
      }
      

      【讨论】:

        【解决方案7】:

        尝试将#import "Product_Module_Name-Swift.h" 添加到您的Product_Module_Name-Prefix.pch 文件中。这为我修复了它,而且您现在可以从任何 objc 文件访问您的 swift 文件。

        【讨论】:

        • 这是一个非常糟糕的建议。创建循环进口的可能性很高。使用预编译的标头只是为了与所有标头共享某些内容是对预编译标头的滥用。现在只需忽略预编译的头文件,不要放任何东西。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-05
        • 2017-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-06
        相关资源
        最近更新 更多