Merge pull request #46 from haolloyin/translate

修改 Mix and Match 章节,完成 Interacting with Objective-C APIs 初稿
This commit is contained in:
梁杰
2014-06-06 23:12:50 +08:00
4 changed files with 554 additions and 0 deletions

View File

@ -0,0 +1,318 @@
--- [@haolloyin](https://github.com/haolloyin) 翻译自苹果官方文档 [Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_75) 的 [Interacting with Objective-C APIs](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html) 章节
## Interacting with Objective-C APIs - 与 Objc API 交互
Swift 和 Objc 之间双向的`互用性``Interoperability`),让你可以用一种语言访问另一种语言写的代码。当你开始整合 Swift 代码到应用的开发工作流时,可以很好地理解这种互用性在重新定义,改善并强化你写 Cocoa 应用的方式。
互用性一个很重要的方面是在写 Swift 代码的同时可以用 Objc API。导入 Objc framework 之后,你可以用 Swift 原生的语法来实例化类,并和它们进行交互。
### Initialization - 初始化
你可以用 Swift 语法调用 Objc 类的初始化方法,在 Swift 代码中进行 Objc 类的实例化。因为 Objc 的初始化方法都迁移到 Swift 了。`init` 前缀被去掉并成为关键字表示一个方法是初始化方法。以 `initWith` 开头的初始化方法中的 `With` 也被去掉了。从 `init``initWith` 中切分出来首字母变成小写,并且作为第一个参数名,该 selector 的其他部分也同样变成参数名。selector 括号内的各部分也是调用端
例如 Objc 中这么写:
```
// OBJECTIVE-C
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
```
Swift 中则这么写:
```
// SWIFT
let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
```
不需要调用 `alloc`Swift 会为你正确处理。**注意调用 Swift 初始化方法时不应该再出现 `init` 字眼。**
初始化时你可以显式指明变量的类型也可以忽略不写Swift 的类型推导会确定对象的类型。
```
// SWIFT
let myTextField = UITextField(frame: CGRect(0.0, 0.0, 200.0, 40.0))
```
`UITableView``UITextField` 拥有和 Objc 类似的方法,可以像在 Objc 代码中那样使用它们来访问在类中定义的属性或方法。
为了一致性和简单性Objc 的工厂方法在 Swift 中以方便的初始化方法出现,这种映射方法使它们和初始化方法一样简洁明了。例如你在 Objc 中这么调用工厂方法:
```
// OBJECTIVE-C
UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
```
在 Swift 中这么调用:
```
// SWIFT
let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)
```
### Accessing Properties - 属性访问
在 Swift 中用点语法来访问、设置属性。
```
// SWIFT
myTextField.textColor = UIColor.darkGrayColor()
myTextField.text = "Hello world"
if myTextField.editing {
myTextField.editing = false
}
```
当获取或设置属性时,直接用属性名,不需要括号。注意 `darkGrayColor``UIColor` 中的方法,不是属性,因此带有括号。
在 Objc 中返回一个值并且不带参数的方法,可以看作隐式的属性访问器,并用和访问属性一样的语法来调用。这在 Swift 中很简单,只有在 Objc 中用 `@property` 语法声明的属性才会被看作属性。方法的导入、调用详见 [Working with Methods](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_29)。
### Working with Methods - 方法调用
在 Swift 中调用 Obc 方法时,使用点语法。
因为 Objc 的方法都迁移到 Swift方法名的第一部分作为基本的方法名并出现在小括号左边。第一个实参写在小括号里面并且不需要参数名方法名的其余部分与实参相应写在小括号里面。方法名的所有部分在调用端都是必须的。
例如 Objc 中这样调用:
```
// OBJECTIVE-C
[myTableView insertSubview:mySubview atIndex:2];
```
在 Swift 中这样调用:
```
// SWIFT
myTableView.insertSubview(mySubview, atIndex: 2)
```
如果调用的方法不带参数,你还是需要写上括号。
```
// SWIFT
myTableView.layoutIfNeeded()
```
### id Compatibility - 兼容 id
Swift 包含一个叫做 `AnyObject` 的协议类型来表示任意一种对象,就像 Objc `id` 一样。`AnyObject` 协议允许你在写类型安全的 Swift 代码的同时,保留无类型(`untyped`)对象的灵活性。正由于 `AnyObjcet` 协议提供的安全性Swift 用 `AnyObject` 替代 `id`
`id` 一样你可以把任何类类型的对象赋值assign给用 `AnyObject` 声明的常量或变量,也可以把变量重新赋值到不同类型的对象。
```
// SWIFT
var myObject: AnyObject = UITableViewCell()
myObject = NSDate()
```
也可以不经过类型转换来调用 Objc 的方法或访问属性(后赋值给常量),但必须是 Objc 中用 `@objc attribute` 标记过的兼容方法。
```
// SWIFT
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
```
然而因为 `AnyObject` 对象只有到运行时才确定其真实类型,这很可能写出不安全的代码。另外对比 Objc ,如果你调用(或访问) `AnyObject` 对象不存在的方法(或属性),这会导致运行时错误。例如下面的代码会编译通过,但是在运行时引发 `unrecognized selector error`
```
// SWIFT
myObject.characterAtIndex(5)
// crash, myObject does't respond to that method
```
但是,你可以在代码中用 Swift 中的`可选值``optional`)来消除这种常见的 Objc error。当你在一个 `AnyObject` 类型的对象上调用一个 Objc 方法时,事实上跟`可选值隐式拆包``implicitly unwrapped optional`)很类似。你可以用跟可选协议中的方法相同的`可选链``optional chaining`)语法来调用 `AnyObject` 对象的方法。这对属性也同样适用。
例如下面的代码第1~2行将不会执行因为 `NSDate` 对象不存在 `length` 属性和 `characterAtIndex:` 方法。常量 `myLength` 将会被推断为可选整型(`optional Int`),并且赋值为 `nil`。你也可以用 `if-let` 语句来尝试性地拆包方法调用的结果因为对象调用的方法可能不存在not respond to正如下面第3行所示
```
// SWIFT
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
```
> 译注:关于可选链(`Optional Chain`),建议读下官方教程的 [Optional Chaining](https://github.com/CocoaChina-editors/Welcome-to-Swift/blob/master/The%20Swift%20Programming%20Language/02Language%20Guide/17Optional%20Chaining.md) 和这篇 “[Swift之 ? 和 !](http://joeyio.com/ios/2014/06/04/swift---/)”。
和 Swfit 中所有向下转型(`downcast`)一样,从 `AnyObject` 转型为具体对象类型是不保证成功的,因此会返回可选值(`optional value`),你可以通过检测可选值来确定转型是否成功。
```
// SWIFT
let userDefaults = NSUserDefaults.standardUserDefaults()
let lastRefreshDate: AnyObject? = userDefaults.objectForKey("LastRefreshDate")
if let date = lastRefreshDate as? NSDate {
println("\(date.timeIntervalSinceReferenceDate)")
}
```
Of course, if you are certain of the type of the object (and know that it is not nil), you can force the invocation with the as operator.
当然如果你确定一个对象的类型(并且对象不为 `nil`),你可以把它作为操作数强行调用。
```
// SWIFT
let myDate = lastRefreshDate as NSDate
let timeInterval = myDate.timeIntervalSinceReferenceDate
```
### Working with nil - 关于 nil
Objc 用原始指针(可能为 `NULL`,即 Objc 中的 `nil`来指向对象。Swift 中的所有值(包括结构体,对象引用)都是非空的(`non-nil`)。作为替代,可以用一个`可选类型``optional type`)来表示一个值,尽管在封包过程中可能会丢失这个值。当值丢失时,得到的是 `nil`。阅读 [Optional](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5) 来了解更多。
**由于 Objc 没有确保对象非空(`non-nil`Swift 在将 Objc API 导入时,将所有实参类型和返回类型都改成可选类型。当你使用 Objc 对象时,应该先检查它是否为 `nil`。**
在某些情况下你可能100%确定一个 Objc 方法或属性不会返回 `nil` 对象引用。为了在这些情景下更方便地使用对象Swift 引入了叫做`隐式拆包可选值``implicitly unwrapped optionals`)的类型,它包含所有可选类型的安全特性。另外,你可以直接访问它的值,不用判断是否为 `nil`或者将它拆包。当你在未经过安全拆包之前访问这种可选类型的值时,隐式拆包可选值会检查值是否已经丢失,如果已经丢失,会发生运行时错误。因此,你应该总是自行检查或拆包一个`隐式拆包可选值`,除非你确定这个值没有丢失。
### Extensions - 扩展
Swift 的扩展(`extension`)类似于 Objc 的类别(`category``extension` 给 Swift 现有的类,结构体,枚举,增加行为,也适用于 Objc 中定义的类、结构体和枚举。你可以给一个类型定义一个 `extension`,不管这个类型来自系统 framework 还是你自定义的类型。简单地导入对应的模块,用你在 Objc 中那样用相同的名字引用类,结构体和枚举。
例如你可以用等边三角型来扩展 `UIBezierPath` 类,基于你提供的边长和起始点创建一个简单的 Bézier 路径。
```
// SWIFT
extension UIBezierPath {
convenience init(triangleSideLength: Float, origin: CGPoint) {
self.init()
let squareRoot = Float(sqrt(3))
let altitude = (squareRoot * triangleSideLength) / 2
moveToPoint(origin)
addLineToPoint(CGPoint(triangleSideLength, origin.x))
addLineToPoint(CGPoint(triangleSideLength / 2, altitude))
closePath()
}
}
```
可以用 `extension` 添加属性(包括类属性,静态属性),但是这些**属性必须进行计算;`extension` 并不能给类型,结构体,枚举添加存储属性(`stored property`)。**
下面的例子扩展了 `CGRect` 结构体,使之拥有一个计算 `area` 的属性:
```
// SWIFT
extension CGRect {
var area: CGFloat {
return width * height
}
}
let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0)
let area = rect.area
// area: CGFloat = 500.0
```
也可以用 `extension` 给类添加协议(`protocol conformance`)而无需继承这个类。如果是 Swift 中定义的协议,你可以将它添加到结构体或者枚举,不管这个结构体或枚举是来自 Swift 还是 Objc。
**不能用 `extension` 对 Objc 类型现有的方法或属性进行重载(`override`)。**
### Closures - 闭包
Objective-C blocks are automatically imported as Swift closures. For example, here is an Objective-C block variable:
Objc 的 `block` 被自动导入为 Swift 的闭包(`closure`)。例如下面的 Objc block 变量:
```
// OBJECTIVE-C
void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) {
/* ... */
}
```
在 Swift 中看起来是这样的:
```
// SWIFT
let completionBlock: (NSData, NSError) -> Void = { data, error in
/* ... */
}
```
Swift 的闭包和 Objc 的 block 是兼容的,所以你可以将 Swift 的闭包作为实参传给 Objc 中期望传入 block 的方法。Swift 的闭包和函数是相同的类型,所以你甚至可以传递 Swift 的函数名。
闭包拥有和 block 相似的捕获语义(`capture semantic`但是有一个关键的不同之处变量是可变的而不是拷贝一个副本。换句话说Swift 中闭包的变量默认等同于 Objc 中用 `__block` 修饰的变量。
### Object Comparison - 对象比较
Swift 中比较两个对象有两种不同的方式。一种是相等(`equality ==`),比较两个对象的内容;另一种是全等(`identity ===`),比较两个常量或变量是否指向同一个的对象实例。
Swift 和 Objc 对象在 Swift 中一般用 `==``===` 操作符进行比较。Swift 为继承自 `NSObject` 类的对象提供了 `==` 操作符的默认实现,即 Swift 会调用 `NSObject` 类定义的 `isEqual:` 方法。`NSObject` 类只会判断是否全等(`identity comparison`,即是否指向同一实例),所以你应该自己实现 `NSObject` 子类的 `isEqual:` 方法。由于你可以传递 Swift 对心爱难过(包括那些没有继承自 `NSObject` 类的类对象)给 Objc API你应该实现 `isEqual:` 方法,以便 Objc API 可以判断两个对象的内容是否相同,而不是判断是否指向同一个实例。
作为类对象判等的一部分,确保根据对象比较的规则来实现 `hash` 属性。进一步说,如果你想用你的类对象作为字典的 key你还要实现 `Hashable` 协议的 `hashValue` 属性。
### Swift Type Compatibility - Swift 类型兼容性
当你在 Swift 中定义一个类继承自 `NSObject` 类或其他任意 Objc 类时,这个类自动与 Objc 兼容。这些步骤由 Swift 编译器为你完成。如果你不打算将一个 Swift 类导入到 Objc那么你不用担心类型兼容相关的问题。另外如果你的 Swift 类没有继承自 Objc 的类,并且你将会在 Objc 代码中使用,那么可以用 `@objc` 来修饰它。
`@objc attribute` 使你的 Swift API 在 Objc 与其运行时中可用。换句话说,你可以用 `@objc` 来修饰任何你想在 Objc 使用的 Swift 类,方法,属性。如果你的 Swift 类继承自 Objc 的类,编译器会自动给 Swift 类插入 `@objc`。编译器也会自动为类的所有方法和属性添加 `@objc`,只要这个类用 `@objc` 修饰了。当你用 `@IBOutlet` `@IBAction` `@NSManaged` 时,`@objc` 也会被自动加上。`@objc` 在你用 Objc 类的 `selector` 实现 `target-action` 设计模式这一类工作时很有用,例如 `NSTimer``UIButton`
当你在 Objc 中使用 Swift API编译器通常直接翻译。例如 Swift API `func playSong(name: String)` 会被导入 Objc 变成 `- (void)playSong:(NSString *)name`。但是有一个例外:当你在 Objc 中使用 Swift 初始化方法时,编译器为你在方法名最前面添加 `initWith` 字样并且适当地将原来初始化方法的首字母大写。例如Swift 的初始化方法 ` init (songName: String, artist: String)` 会被导入 Objc 变成 `(instancetype)initWithSongName:(NSString *)songName artist:(NSString *)artist`
Swift 也提供一个 `@objc` 的变型类允许你指定 Objc 的符号名(`symbol`)。例如,如果你的 Swift 类名含有 Objc 不支持的字符,你可以指定一个替代名以便在 Objc 中使用。如果你想为 Swift 函数提供一个 Objc 名称,应该使用 Objc 的 `selector` 语法,要记得为 `selector` 的每一部分加上分号(`:`)。
```
// SWIFT
@objc(Squirrel)
class Белка {
@objc(initWithName:)
init (имя: String) { /*...*/ }
@objc(hideNuts:inTree:)
func прячьОрехи(Int, вДереве: Дерево) { /*...*/ }
}
```
当你给 Swift 类使用 `@objc(<#name#>)` 时,这个类可以在 Objc 中使用,并且不需要任何命名空间。因此 `@objc(<#name#>)` 在你迁移可存档的(`archivable` Objc 类到 Swift 时很有用,因为被存档的(`archived`)对象在存档中保存了它们的类名,你在 Objc 类中应该用 `@objc(<#name#>)` 来指定同样的名字,使得旧的存档可以在你新的 Swift 类中反存档(`unarchived`)。
### Objective-C Selectors
Objc 的 `selector` 是一种指向 Objc 方法名的类型。在 Swift 中Objc 的 `selector` 相应地用 `Selector` 结构体表示。你可以用 string 字面量来构造一个 Swift 的 `Selector`,例如:`let mySelector: Selector = "tappedButton:"`。由于 string 字面量会自动转化为 `selector`,因此你可以传递一个 string 字面量给任何接受 `selector` 作为参数的方法。
```
// SWIFT
import UIKit
class MyViewController: UIViewController {
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibName, bundle: nibBundle)
myButton.targetForAction("tappedButton:", withSender: self)
}
func tappedButton(sender: UIButton!) {
println("tapped button")
}
}
```
> **注意**
>
> **`performSelector:` 方法和其他与方法调用(`selector-invoking`)相关的方法没有被迁移到 Swift因为他们固有的不安全性。**
如果你的 Swift 类继承自 Objc 的类,那么这个类的所有方法和属性都是对 Objc `selector` 可见的。反之,如果 Swift 类没有继承自 Objc 类,你需要加上 `@objc` 修饰符,使得它们成为 Objc 中可用的 `selector`,详见前面的 [Swift Type Compatibility](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_36) 一节。

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,236 @@
--- [@haolloyin](https://github.com/haolloyin) 翻译自苹果官方文档 [Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_75) 的 [Mix and Match](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html) 章节
# 在同个工程中使用 Swift 和 Objc
Swift 与 Objc 的兼容能力使你可以在同一个工程中同时使用两种语言。你可以用这种叫做 `mix and match` 的特性来开发基于混合语言的应用,可以用 Swfit 的最新特性实现应用的一部分功能,并无缝地并入已有的 Objc 的代码中。
## Mix and Match 概述
Objc 和 Swift 文件可以在一个工程中并存,不管这个工程原本是基于 Objc 还是 Swift。你可以直接往现有工程中简单地添加另一种语言的源文件。这种自然的工作流使得创建混合语言的应用或框架 target与用单独一种语言时一样简单。
混合语言的工作流程只有一点点区别,这取决于你是在写应用还是写框架。下面描述了普通的用两种语言在一个 target 中导入模型的情况,后续章节会有更多细节。
![DAG_2x.png](https://raw.githubusercontent.com/haolloyin/the-swift-programming-language-in-chinese/translate/Using%20Swift%20with%20Cocoa%20and%20ObjectiveC/chapter3/DAG_2x.png?raw=true)
## 在同个应用的 target 中导入
如果你在写混合语言的应用,可能需要用 Swift 代码访问 Objc 代码,或者反之。下面的流程描述了在非框架 target 中的应用。
### 将 Objc 导入 Swift
在一个应用的 target 中导入一些 Objc 文件供 Swift 代码使用时,你需要依赖与 Objc 的桥接头文件(`bridging header`)来暴露给 Swift。当你添加 Swift 文件到现有的 Objc 应用或反之Xcode 会自动创建这些头文件。
![bridgingheader_2x.png](https://raw.githubusercontent.com/haolloyin/the-swift-programming-language-in-chinese/translate/Using%20Swift%20with%20Cocoa%20and%20ObjectiveC/chapter3/bridgingheader_2x.png?raw=true)
如果你同意Xcode 会在源文件创建的同时生成头文件,并用 product 的模块名加上 `-Bridging-Header.h` 命名。关于 product 的模块名,详见 [Naming Your Product Module](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_85)。
你应该编辑这个头文件来对 Swift 暴露出 Objc 代码。
#### 在同一target中将Objective-C代码导入到Swift中
1.在 Objc 桥接头文件中import 任何你想暴露给 Swift 的头文件,例如:
```
// OBJECTIVE-C
#import "XYZCustomCell.h"
#import "XYZCustomView.h"
#import "XYZCustomViewController.h"
```
2.确保在 `Build Settings` 中 Objc 桥接头文件的 `build setting` 是基于 Swfit 编译器,即 `Code Generation` 含有头文件的路径。这个路径必须是头文件自身的路径,而不是它所在的目录。
这个路径应该是你工程的相对路径,类似 `Info.plist``Build Settings` 中指定的路径。在大多数情况下,你不需要修改这个设置。
在这个桥接头文件中列出的所有 public 的 Objc 头文件都会对 Swift 可见。之后当前 target 的所有 Swift 文件都可以使用这些头文件中的方法,不需要任何 import 语句。用 Swift 语法使用这些 Objc 代码,就像使用系统自带的 Swift 类一样。
```
// SWIFT
let myCell = XYZCustomCell()
myCell.subtitle = "A custom cell"
```
### 将 Swift 导入 Objc
向 Objc 中导入Swift 代码时,你依赖 Xcode 生成的头文件来向 Objc 暴露 Swift 代码。这是自动生成 Objc 头文件,它包含了你的 target 中所有 Swift 代码中定义的接口。可以把这个 Objc 头文件看作 Swift 代码的 `umbrella header`。它以 product 模块名加 `-Swift.h` 来命名。关于 product 的模块名,详见[Naming Your Product Module](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_85)。
你不需要做任何事情来生成这个头文件,只需要将它导入到你的 Objc 代码来使用它。注意这个头文件中的 Swift 接口包含了它所使用到的所有 Objc 类型。如果你在 Swift 代码中使用你自己的 Objc 类型,确保先将对应的 Objc 头文件导入到你的 Swift 代码中,然后才将 Swift 自动生成的头文件导入到 Objc .m 源文件中来访问 Swift 代码。
##### 在同一target中将Swift代码导入到Objective-C中
- 在相同 target 的 Objc .m 源文件中用下面的语法来导入Swift 代码:
```
// OBJECTIVE-C
#import "ProductModuleName-Swift.h"
```
target 中任何 Swift 文件将会对 Objc .m 源文件可见,包括这个 import 语句。关于在 Objc 代码中使用 Swift 代码,详见 [Using Swift from Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_84)。
| | 导入到 Swift | 导入到 Swift |
| -------------|:-----------:|:------------:|
| Swift 代码 | 不需要import语句 | #import <ProductName/ProductModuleName-Swift.h> |
| Objc 代码 | 不需要import语句需要 Objc `umbrella头文件| #import "Header.h" |
## 在同个 Framework 的 target 中导入
如果你在写一个混合语言的框架,可能会从 Swift 代码访问 Objc 代码,或者反之。
### 将 Objc 导入 Swift
要将一些 Objc 文件导入到同个框架 target 的 Swift 代码中去,你需要将这些文件导入到 Objc 的 `umbrella header` 来供框架使用。
##### 在同一framework中将Objective-C代码导入到Swift中
确保将框架 target 的 `Build Settings > Packaging > Defines Module` 设置为 `Yes`。然后在你的 `umbrella header` 头文件中导入你想暴露给 Swift 访问的 Objc 头文件,例如:
```
// OBJECTIVE-C
#import <XYZ/XYZCustomCell.h>
#import <XYZ/XYZCustomView.h>
#import <XYZ/XYZCustomViewController.h>
```
Swift 将会看到所有你在 `umbrella header` 中公开暴露出来的头文件,框架 target 中的所有 Swift 文件都可以访问你 Objc 文件的内容,不需要任何 import 语句。
```
// SWIFT
let myCell = XYZCustomCell()
myCell.subtitle = "A custom cell"
```
### 将 Swift 导入 Objc
要将一些 Swift 文件导入到同个框架的 target 的 Objc 代码去,你不需要导入任何东西到 `umbrella header` 文件,而是将 Xcode 为你的 Swift 代码自动生成的头文件导入到你的 Obj .m 源文件去,以便在 Objc 代码中访问 Swift 代码。
##### 在同一framework中将Swift代码导入到Objective-C中
确保将框架 target 的 `Build Settings > Packaging` 中的 `Defines Module` 设置为 `Yes`。用下面的语法将 Swift 代码导入到同个框架 target 下的 Objc .m 源文件去。
```
// OBJECTIVE-C
#import <ProductName/ProductModuleName-Swift.h>
```
这个 import 语句所包含的 Swift 文件都可以被同个框架 target 下的 Objc .m 源文件访问。关于在 Objc 代码中使用 Swift 代码,详见 [Using Swift from Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_84)。
| | 导入到 Swift | 导入到 Swift |
| -------------|:-----------:|:------------:|
| Swift 代码 | 不需要import语句 | #import <ProductName/ProductModuleName-Swift.h> |
| Objc 代码 | 不需要import语句需要 Objc `umbrella头文件| #import "Header.h" |
## 导入外部 Framework
你可以导入外部框架,不管这个框架是纯 Objc纯 Swift还是混合语言的。import 外部框架的流程都是一样的,不管这个框架是用一种语言写的,还是包含两种语言。当你导入外部框架时,确保 `Build Setting > Pakaging > Defines Module` 设置为 `Yes`
用下面的语法将框架导入到不同 target 的 Swift 文件中:
```
// SWIFT
import FrameworkName
```
用下面的语法将框架导入到不同 target 的 Objc .m 文件中:
```
OBJECTIVE-C
@import FrameworkName;
```
| | 导入到 Swift | 导入到 Objc |
| -------------|:-----------:|:------------:|
|任意语言框架 | import FrameworkName | @import FrameworkName; |
## 在 Objc 中使用 Swift
当你将 Swift 代码导入 Objc 文件之后,用普通的 Objc 语法使用 Swift 类。
```
// OBJECTIVE-C
MySwiftClass *swiftObject = [[MySwiftClass alloc] init];
[swiftObject swiftMethod];
```
Swift 的类或协议必须用 `@objc attribute` 来标记,以便在 Objc 中可访问。这个 attribute 告诉编译器这个 Swift 代码可以从 Objc 代码中访问。如果你的 Swift 类是 Objc 类的子类,编译器会自动为你添加 `@objc attribute`。详见 [Swift Type Compatibility](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_36)。
你可以访问 Swift 类或协议中用 `@objc attribute` 标记过东西,只要它和 Objc 兼容。不包括一下这些 Swift 独有的特性:
- Generics - 范型
- Tuples - 元组
- Enumerations defined in Swift - Swift 中定义的枚举
- Structures defined in Swift - Swift 中定义的结构体
- Top-level functions defined in Swift - Swift Swift 中定义的顶层函数
- Global variables defined in Swift - Swift 中定义的全局变量
- Typealiases defined in Swift - Swift 中定义的类型别名
- Swift-style variadics - Swift风格可变参数
- Nested types - 嵌套类型
- Curried functions - 柯里化后的函数
例如带有范型类型作为参数,或者返回元组的方法不能在 Objc 中使用。
为了避免循环引用,不要将 Swift 代码导入到 Objc 头文件中。但是你可以在 Objc 头文件中前向声明(`forward declare`)一个 Swift 类来使用它,然而,注意**不能在 Objc 中继承一个 Swift 类**。
### 在Objective-C头文件中引用Swift类
这样前向声明 Swift 类:
```
// OBJECTIVE-C
// MyObjcClass.h
@class MySwiftClass;
@interface MyObjcClass : NSObject
- (MySwiftClass *)returnSwiftObject;
/* ... */
@end
```
## Product 模块命名
Xcode 为 Swift 代码生成的头文件的名称,以及 Xcode 创建的 Objc 桥接头文件名称,都是从你的 product 模块名生成的。默认你的 product 模块名和 product 名一样。然而,如果你的 product 名有特殊字符nonalphanumeric非数字、字母的字符例如点号那么它们会被下划线`_`)替换之后作为你的 product 模块名。如果 product 名以数字开头,那么第一个数字会用下划线替换掉。
你可以给 product 模块名提供一个自定义的名称Xcode 会用这个名称来命名桥接的和自动生成的头文件。你只需要在修改在 `build setting` 中的 `Product Module Name` 即可。
## 故障排除提示提醒
- 把 Swift 和 Objc 文件看作相同的代码集合,并注意命名冲突;
- 如果你用框架,确保 `Build Setting > Pakaging > Defines Module` 设置为 `Yes`
- 如果你使用 Objc 桥接头文件,确保在 `Build Settings` 中 Objc 桥接头文件的 `build setting` 是基于 Swfit 编译器,即 `Code Generation` 含有头文件的路径。这个路径必须是头文件自身的路径,而不是它所在的目录。
- Xcode 使用你的 product 模块名,而不是 target 名来命名 Objc 桥接头文件和为 Swift 自动生成的头文件。详见 [Naming Your Product Module](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_85)
- 为了在 Objc 中可用, Swift 类必须是 Objc 类的子类,或者用 `@objc` 标记;
- 当你将 Swift 导入到 Objc 中时,记住 Objc 不会将 Swift 独有的特性翻译成 Objc 对应的特性。详见列表 [Using Swift from Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_84);
- 如果你在 Swift 代码中使用你自己的 Objc 类型,确保先将对应的 Objc 头文件导入到你的 Swift 代码中,然后才将 Swift 自动生成的头文件 import 到 Objc .m 源文件中来访问 Swift 代码。