如何从Swift调用Objective-C代码?

1133

在Swift中,如何调用Objective-C代码?

苹果公司提到它们可以共存于一个应用程序中,但这是否意味着在构建Swift新类的同时,可以技术上重用以前使用Objective-C创建的旧类?


14
请查看苹果的指南,了解如何使用Swift与Cocoa和Objective-C - rickster
2
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_75 - John Riselvato
1
@AnalogFile:1)文档资源的重复在StackOverflow上是可以接受的。显然,我不是唯一一个有疑问的人,因为大多数人都是从Google来到这里的。2)请阅读赏金描述;-) - David Mulder
6
@EvolGate: 究竟是什么缺少使得Objective C不能成为一种跨平台语言呢?该语言及其标准库都是开源的。你可以在Windows、Os X、Linux、BSD、Solaris以及任何其他由GCC或LLVM支持的平台上使用Objective C。即使某些平台不受GCC或LLVM支持,你也可以轻松地将Objective C移植到任何拥有良好C编译器的平台上。我认为它已经非常跨平台了。 - Analog File
3
@DavidMulder:还需要“推理”部分吗? - franck
显示剩余6条评论
17个回答

1555

在Swift中使用Objective-C类

如果您有一个现有的类想要使用,请执行第2步,然后跳转到第5步。(某些情况下,我需要向旧的Objective-C文件添加显式的#import <Foundation/Foundation.h>。)

第1步:添加Objective-C实现--.m

.m文件添加到您的类中,并将其命名为CustomObject.m

第2步:添加桥接头文件

当添加.m文件时,可能会弹出如下提示:

来自Xcode的macOS样式对话框,询问您是否“想配置一个Objective-C桥接头文件”

点击

如果您没有看到提示,或意外删除了桥接头文件,请添加一个新的.h文件到您的项目中,并将其命名为<#YourProjectName#>-Bridging-Header.h

在某些情况下,特别是在使用Objective-C框架时,您不会显式地添加Objective-C类,而且Xcode无法找到链接器。在这种情况下,请创建如上所述命名的.h文件,然后确保您在目标项目设置中链接其路径,如下所示:

演示以上段落的动画

注意:

最佳实践是使用$(SRCROOT)宏链接您的项目,这样如果您移动项目或与使用远程存储库的其他人一起工作,则仍将正常工作。 $(SRCROOT)可以被认为是包含您的.xcodeproj文件的目录。可能会像这样:

$(SRCROOT)/Folder/Folder/<#YourProjectName#>-Bridging-Header.h

第三步:添加Objective-C头文件 -- .h

再添加一个.h文件,将其命名为CustomObject.h

第四步:构建你的Objective-C类

CustomObject.h中进行操作。

#import <Foundation/Foundation.h>

@interface CustomObject : NSObject

@property (strong, nonatomic) id someProperty;

- (void) someMethod;

@end

CustomObject.m

#import "CustomObject.h"

@implementation CustomObject 

- (void) someMethod {
    NSLog(@"SomeMethod Ran");
}

@end

步骤5:为桥接头文件添加类

YourProject-Bridging-Header.h中:

#import "CustomObject.h"

步骤6:使用你的对象

SomeSwiftFile.swift中:

var instanceOfCustomObject = CustomObject()
instanceOfCustomObject.someProperty = "Hello World"
print(instanceOfCustomObject.someProperty)
instanceOfCustomObject.someMethod()

不需要显式导入; 这就是桥接头文件的作用。

在Objective-C中使用Swift类

步骤1:创建新的Swift类

向您的项目添加一个.swift文件,并将其命名为MySwiftObject.swift

MySwiftObject.swift中:

import Foundation

@objc(MySwiftObject)
class MySwiftObject : NSObject {

    @objc
    var someProperty: AnyObject = "Some Initializer Val" as NSString

    init() {}

    @objc
    func someFunction(someArg: Any) -> NSString {
        return "You sent me \(someArg)"
    }
}

步骤2:将Swift文件导入ObjC类

SomeRandomClass.m中:

#import "<#YourProjectName#>-Swift.h"

文件:<#YourProjectName#>-Swift.h应该已经自动创建在您的项目中,即使您看不到它。

步骤3:使用您的类

MySwiftObject * myOb = [MySwiftObject new];
NSLog(@"MyOb.someProperty: %@", myOb.someProperty);
myOb.someProperty = @"Hello World";
NSLog(@"MyOb.someProperty: %@", myOb.someProperty);

NSString * retString = [myOb someFunctionWithSomeArg:@"Arg"];

NSLog(@"RetString: %@", retString);

注释:

  1. 如果代码补全没有按照你的期望行事,请尝试使用 R 进行快速构建,帮助Xcode在Swift上下文和Objective-C之间找到一些Objective-C代码。反之亦然。

  2. 如果您将一个 .swift 文件添加到旧项目中,并出现错误 dyld: Library not loaded: @rpath/libswift_stdlib_core.dylib,请尝试完全重新启动Xcode

  3. 原来可以使用纯Swift类(非 NSObject 的子类)并使用 @objc 前缀使其在Objective-C中可见,但是现在不再支持此操作。现在,要在Objective-C中可见,Swift对象必须要么是遵循 NSObjectProtocol 协议的类(最简单的方法是继承自 NSObject),要么是标记为 @objc 并具有某个整数类型(如 Int)的原始值的 enum您可以查看Swift 1.x代码的编辑历史记录,其中使用了没有这些限制的 @objc 示例。


6
重要的是,你需要使用@objc给方法加注释,否则Objective-C将无法看到Swift方法。 - Tomáš Linhart
31
你是正确的。只有当你不使用Cocoa对象时,你才需要指定它。为了在Objective-C中可访问和可用,Swift类必须是Objective-C类的后代或标记为 @objc。 - Tomáš Linhart
6
如果将一个 Objective C 框架导入到 Swift 中,请确保将 Obj C 框架所依赖的所有框架导入到您的项目中(在 Build Phases -> Link Binary With Libraries 中),然后将这些框架的 #import 添加到前缀头文件中,该文件必须在构建设置中添加到您的项目中(在 Prefix Header 字段中)。这包括像 UIKit 和 Foundation 这样的框架,即使这些框架已经从 Swift 中使用。这个问题困扰了我数小时,似乎没有人记录下这些步骤。 - user1021430
2
@lee - 你能否进入构建设置,确保你的“产品模块名称”是你所期望的吗?语法应该是#import "ProductModuleName-Swift.h",它默认为项目名称,大多数人不会更改它。 - Logan
2
谢谢@Logan,这对我有用,但我注意到的唯一不同之处是,在xcode 7.3中添加.m或.h文件时,没有弹出窗口来创建桥接头文件,您必须手动转到“构建设置”并执行它,或者我的项目设置可能有问题 :)。 - AmJa
显示剩余22条评论

133
请参阅苹果公司关于在 Cocoa 和 Objective-C 中使用 Swift 的指南。该指南涵盖了如何从 Swift 中使用 Objective-C 和 C 代码,以及反之,还提供了如何将项目转换或混合使用 Objective-C/C 和 Swift 部分的建议。
编译器会自动生成用于调用 C 函数和 Objective-C 方法的 Swift 语法。正如文档中所示,这个 Objective-C:
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];

转换为这个 Swift 代码:

let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)

Xcode也可以实时进行这种转换——您可以在编辑Swift文件时使用“快速打开”并输入Objective-C类名,它将带您到类头的Swift版本。(您还可以通过在Swift文件中cmd-click API符号来获取此功能。)而且,在iOS 8OS X v10.10 (Yosemite)开发人员库中的所有API参考文档都以Objective-C和Swift形式可见(例如UIView)。

3
如下是与Swift集成到现有项目中的Apple文档直接链接:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_75 - skywinder
如果您对为什么需要头文件以及在链接阶段如何使用它感兴趣,请务必阅读此《手动Swift:了解Swift/Objective-C构建流水线》(Manual Swift: Understanding the Swift/Objective-C Build Pipeline)链接 - mfaani
更简单的写法:let myTableView = UITableView(...) - gnasher729

70
以下是使用Objective-C代码(在这种情况下,由第三方提供的框架)在Swift项目中的逐步说明:
  1. 通过选择“文件 -> 新建 -> 新建文件 -> Objective-C文件”,将任何Objective-C文件添加到您的Swift项目中。保存后,Xcode会询问是否要添加桥接头文件。选择“”。 Gif: adding empty file to project and generating bridging header
    (来源:derrrick.com
简单来说:
  1. 出现提示,然后点击“确定”…如果没有出现,则可以手动创建如下…从iOS源代码中创建一个头文件,并将其命名为ProjectName-Bridging-Header(例如:Test-Bridging-Header),然后转到Swift编译器代码的构建设置->Objective-C桥接添加Objective-C桥接名称..(Test/Test-Bridging-Header.h)。是的,完成了。

  2. 可选地,删除您添加的Objective-C文件(在上面的GIF图像中命名为“anything”)。 您不再需要它。

  3. 打开桥接头文件--文件名的格式为[YourProject]-Bridging-Header.h。 它包括Xcode提供的注释。 添加一行代码以包含要包含的Objective-C文件,例如第三方框架。 例如,要将Mixpanel添加到项目中,您需要向桥接头文件添加以下代码行:

    #import "Mixpanel.h"
  4. 现在,在任何Swift文件中,您都可以使用现有的Objective-C代码,使用Swift语法(在此示例中,您可以调用Mixpanel SDK方法等)。 您需要熟悉Xcode如何将Objective-C翻译成Swift。 苹果公司的指南是一个快速阅读。 或者查看此答案以获取不完整的摘要。

的示例:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    Mixpanel.sharedInstanceWithToken("your-token")
    return true
}

Format:

这就是全部内容!

注意:如果从项目中删除桥接头文件,请确保进入“构建设置”并删除“Swift编译器 - 代码生成”下的“Objective-C桥接头文件”的值。


45

你可以阅读这篇不错的文章:Swift & Cocoapods。基本上,我们需要创建一个桥接头文件并把所有 Objective-C 头文件放在里面。然后我们需要在构建设置中引用它。之后,我们就可以使用 Objective-C 代码了。

let manager = AFHTTPRequestOperationManager()
manager.GET(
  "http://example.com/resources.json",
  parameters: nil,
  success: { (operation: AFHTTPRequestOperation!,
              responseObject: AnyObject!) in
      println("JSON: " + responseObject.description)
  },
  failure: { (operation: AFHTTPRequestOperation!,
              error: NSError!) in
      println("Error: " + error.localizedDescription)
  })

还要查看苹果公司的文档 使用Swift与Cocoa和Objective-C


30
我编写了一个简单的Xcode 6项目,展示了如何混合使用C++、Objective-C和Swift代码:
具体来说,示例中从Swift调用了一个Objective-C和一个C++函数
关键是创建一个共享头文件Project-Bridging-Header.h,并将Objective-C头文件放在那里。
请下载完整的项目作为示例。 https://github.com/romitagl/shared/tree/master/C-ObjC-Swift/Performance_Console

30

我还想在这里补充一件事:

非常感谢@Logan的答案。它对创建桥接文件和设置有很大帮助。

但是,即使完成了所有这些步骤,我仍然无法在Swift中获得Objective-C类。

我使用了库,并将其集成到我的项目中。该库是"pop"。

因此,如果您正在Swift中使用Objective-C pods,则可能无法获得或导入这些类。

您所需要做的简单的事情是:

  1. 进入<YOUR-PROJECT>-Bridging-Header文件并
  2. 将语句#import <ObjC_Framework>替换为@import ObjC_Framework

例如:(Pop library)

#import <pop/POP.h>

与,以及

@import pop;

#import 无法工作时,请使用 clang import


当 #import 无法工作时,请使用 clang import。什么?难道不是应该使用 @import 吗? - mfaani

28

引用自文档

任何作为模块可访问的Objective-C框架(或C库)都可以直接导入Swift。这包括所有Objective-C系统框架,例如Foundation、UIKit和SpriteKit,以及系统附带的常见C库。例如,要导入Foundation,只需将以下导入语句添加到您正在工作的Swift文件的顶部:

import Foundation

这个导入使得所有Foundation API,包括NSDate、NSURL、NSMutableData以及它们的方法、属性和类别在Swift中直接可用。


17

混合使用Objective-C和Swift

高级别示意图:

同一目标

//Objective-C exposes API for Swift through <target_name>-Bridging-Header.h
.swift uses .h.m = Swift consumer, Objective-C producer = <target_name>-Bridging-Header.h

//Swift exposes API for Objective-C through <target_name>-Swift.h
.h.m uses .swift = Objective-C consumer, Swift producer = <target_name>-Swift.h

Swift消费者,Objective-C生产者

  1. 添加新的头文件.h和实现文件.m - Cocoa类文件(Objective-C)
    例如<MyFileName>.h<MyFileName>.m

  2. 配置桥接头文件
    当你看到Would you like to configure an Objective-C bridging header时,点击 - Yes

    • <target_name>-Bridging-Header.h会自动生成
    • Build Settings -> Objective-C Bridging Header(SWIFT_OBJC_BRIDGING_HEADER)
  3. 将类添加到桥接头文件中
    <target_name>-Bridging-Header.h文件中添加一行#import "<MyFileName>.h"

之后您可以使用SwiftObjective-C's MyFileName.hMyFileName.m

P.S.如果您应该将一个已存在的Objective-C文件添加到Swift项目中,请先添加Bridging-Header.h并导入它

Objective-C消费者,Swift生产者

  1. 添加一个<MyFileName>.swift并扩展NSObject

  2. 将Swift文件导入到ObjC类中
    #import "<target_name>-Swift.h"添加到您的Objective-C文件中

  3. 通过@objc公开Swift代码 [About]

之后,您就可以使用来自Objective-CSwift's <MyFileName>.swift

[<product_name>-Swift.h file not found]

不同的目标 [.modulemap][umbrella.h]用于公开Swift的Objective-C。而Swift.h用于公开Objective-C的Swift代码

[词汇表]


16

对于任何想要向Swift添加Objective-C库的人,请注意:您应该在构建设置->链接->其他链接器标志中添加-ObjC


为什么?请添加有关此标志的信息以及为什么应该添加它。 - Johan Karlsson

14
在创建了桥接头文件后,进入"Build Settings" => 搜索"Objective-C Bridging Header"。
然后,在下方找到 "Objective-C Generated Interface Header Name" 文件。
将该文件导入到您的视图控制器中。
例如:在我的情况下:"Dauble-Swift.h"
参考截图:eEter image description here

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