Skip to content

Commit 225d88d

Browse files
committed
修改章节目录名称;完成 Interacting with Objective-C APIs 初稿
1 parent f9cc9d2 commit 225d88d

File tree

4 files changed

+318
-0
lines changed

4 files changed

+318
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
--- [@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) 章节
2+
3+
4+
## Interacting with Objective-C APIs - 与 Objc API 交互
5+
6+
Swift 和 Objc 之间双向的`互用性``Interoperability`),让你可以用一种语言访问另一种语言写的代码。当你开始整合 Swift 代码到应用的开发工作流时,可以很好地理解这种互用性在重新定义,改善并强化你写 Cocoa 应用的方式。
7+
8+
互用性一个很重要的方面是在写 Swift 代码的同时可以用 Objc API。导入 Objc framework 之后,你可以用 Swift 原生的语法来实例化类,并和它们进行交互。
9+
10+
### Initialization - 初始化
11+
12+
你可以用 Swift 语法调用 Objc 类的初始化方法,在 Swift 代码中进行 Objc 类的实例化。因为 Objc 的初始化方法都迁移到 Swift 了。`init` 前缀被去掉并成为关键字表示一个方法是初始化方法。以 `initWith` 开头的初始化方法中的 `With` 也被去掉了。从 `init``initWith` 中切分出来首字母变成小写,并且作为第一个参数名,该 selector 的其他部分也同样变成参数名。selector 括号内的各部分也是调用端
13+
14+
例如 Objc 中这么写:
15+
16+
```
17+
// OBJECTIVE-C
18+
19+
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
20+
```
21+
22+
Swift 中则这么写:
23+
24+
```
25+
// SWIFT
26+
27+
let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
28+
```
29+
30+
不需要调用 `alloc`,Swift 会为你正确处理。**注意调用 Swift 初始化方法时不应该再出现 `init` 字眼。**
31+
32+
初始化时你可以显式指明变量的类型,也可以忽略不写,Swift 的类型推导会确定对象的类型。
33+
34+
```
35+
// SWIFT
36+
37+
let myTextField = UITextField(frame: CGRect(0.0, 0.0, 200.0, 40.0))
38+
```
39+
40+
`UITableView``UITextField` 拥有和 Objc 类似的方法,可以像在 Objc 代码中那样使用它们来访问在类中定义的属性或方法。
41+
42+
为了一致性和简单性,Objc 的工厂方法在 Swift 中以方便的初始化方法出现,这种映射方法使它们和初始化方法一样简洁明了。例如你在 Objc 中这么调用工厂方法:
43+
44+
```
45+
// OBJECTIVE-C
46+
47+
UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
48+
```
49+
50+
在 Swift 中这么调用:
51+
52+
```
53+
// SWIFT
54+
55+
let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)
56+
```
57+
58+
### Accessing Properties - 属性访问
59+
60+
在 Swift 中用点语法来访问、设置属性。
61+
62+
```
63+
// SWIFT
64+
65+
myTextField.textColor = UIColor.darkGrayColor()
66+
myTextField.text = "Hello world"
67+
if myTextField.editing {
68+
myTextField.editing = false
69+
}
70+
```
71+
72+
当获取或设置属性时,直接用属性名,不需要括号。注意 `darkGrayColor``UIColor` 中的方法,不是属性,因此带有括号。
73+
74+
在 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)
75+
76+
### Working with Methods - 方法调用
77+
78+
在 Swift 中调用 Obc 方法时,使用点语法。
79+
80+
因为 Objc 的方法都迁移到 Swift,方法名的第一部分作为基本的方法名,并出现在小括号左边。第一个实参写在小括号里面,并且不需要参数名;方法名的其余部分与实参相应写在小括号里面。方法名的所有部分在调用端都是必须的。
81+
82+
例如 Objc 中这样调用:
83+
84+
```
85+
// OBJECTIVE-C
86+
87+
[myTableView insertSubview:mySubview atIndex:2];
88+
```
89+
90+
在 Swift 中这样调用:
91+
92+
```
93+
// SWIFT
94+
95+
myTableView.insertSubview(mySubview, atIndex: 2)
96+
```
97+
98+
如果调用的方法不带参数,你还是需要写上括号。
99+
100+
```
101+
// SWIFT
102+
103+
myTableView.layoutIfNeeded()
104+
```
105+
106+
### id Compatibility - 兼容 id
107+
108+
Swift 包含一个叫做 `AnyObject` 的协议类型来表示任意一种对象,就像 Objc `id` 一样。`AnyObject` 协议允许你在写类型安全的 Swift 代码的同时,保留无类型(`untyped`)对象的灵活性。正由于 `AnyObjcet` 协议提供的安全性,Swift 用 `AnyObject` 替代 `id`
109+
110+
`id` 一样,你可以把任何类类型的对象赋值(assign)给用 `AnyObject` 声明的常量或变量,也可以把变量重新赋值到不同类型的对象。
111+
112+
```
113+
// SWIFT
114+
115+
var myObject: AnyObject = UITableViewCell()
116+
myObject = NSDate()
117+
```
118+
119+
也可以不经过类型转换来调用 Objc 的方法或访问属性(后赋值给常量),但必须是 Objc 中用 `@objc attribute` 标记过的兼容方法。
120+
121+
```
122+
// SWIFT
123+
124+
let futureDate = myObject.dateByAddingTimeInterval(10)
125+
let timeSinceNow = myObject.timeIntervalSinceNow
126+
```
127+
128+
然而因为 `AnyObject` 对象只有到运行时才确定其真实类型,这很可能写出不安全的代码。另外对比 Objc ,如果你调用(或访问) `AnyObject` 对象不存在的方法(或属性),这会导致运行时错误。例如下面的代码会编译通过,但是在运行时引发 `unrecognized selector error`
129+
130+
```
131+
// SWIFT
132+
133+
myObject.characterAtIndex(5)
134+
// crash, myObject does't respond to that method
135+
```
136+
137+
但是,你可以在代码中用 Swift 中的`可选值``optional`)来消除这种常见的 Objc error。当你在一个 `AnyObject` 类型的对象上调用一个 Objc 方法时,事实上跟`可选值隐式拆包``implicitly unwrapped optional`)很类似。你可以用跟可选协议中的方法相同的`可选链``optional chaining`)语法来调用 `AnyObject` 对象的方法。这对属性也同样适用。
138+
139+
例如下面的代码,第1~2行将不会执行,因为 `NSDate` 对象不存在 `length` 属性和 `characterAtIndex:` 方法。常量 `myLength` 将会被推断为可选整型(`optional Int`),并且赋值为 `nil`。你也可以用 `if-let` 语句来尝试性地拆包方法调用的结果,因为对象调用的方法可能不存在(not respond to),正如下面第3行所示:
140+
141+
```
142+
// SWIFT
143+
144+
let myLength = myObject.length?
145+
let myChar = myObject.characterAtIndex?(5)
146+
if let fifthCharacter = myObject.characterAtIndex(5) {
147+
println("Found \(fifthCharacter) at index 5")
148+
}
149+
```
150+
151+
> 译注:关于可选链(`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---/)”。
152+
153+
和 Swfit 中所有向下转型(`downcast`)一样,从 `AnyObject` 转型为具体对象类型是不保证成功的,因此会返回可选值(`optional value`),你可以通过检测可选值来确定转型是否成功。
154+
155+
```
156+
// SWIFT
157+
158+
let userDefaults = NSUserDefaults.standardUserDefaults()
159+
let lastRefreshDate: AnyObject? = userDefaults.objectForKey("LastRefreshDate")
160+
if let date = lastRefreshDate as? NSDate {
161+
println("\(date.timeIntervalSinceReferenceDate)")
162+
}
163+
```
164+
165+
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.
166+
167+
当然如果你确定一个对象的类型(并且对象不为 `nil`),你可以把它作为操作数强行调用。
168+
169+
```
170+
// SWIFT
171+
172+
let myDate = lastRefreshDate as NSDate
173+
let timeInterval = myDate.timeIntervalSinceReferenceDate
174+
```
175+
176+
### Working with nil - 关于 nil
177+
178+
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) 来了解更多。
179+
180+
**由于 Objc 没有确保对象非空(`non-nil`),Swift 在将 Objc API 导入时,将所有实参类型和返回类型都改成可选类型。当你使用 Objc 对象时,应该先检查它是否为 `nil`**
181+
182+
在某些情况下,你可能100%确定一个 Objc 方法或属性不会返回 `nil` 对象引用。为了在这些情景下更方便地使用对象,Swift 引入了叫做`隐式拆包可选值``implicitly unwrapped optionals`)的类型,它包含所有可选类型的安全特性。另外,你可以直接访问它的值,不用判断是否为 `nil`或者将它拆包。当你在未经过安全拆包之前访问这种可选类型的值时,隐式拆包可选值会检查值是否已经丢失,如果已经丢失,会发生运行时错误。因此,你应该总是自行检查或拆包一个`隐式拆包可选值`,除非你确定这个值没有丢失。
183+
184+
185+
### Extensions - 扩展
186+
187+
Swift 的扩展(`extension`)类似于 Objc 的类别(`category`),`extension` 给 Swift 现有的类,结构体,枚举,增加行为,也适用于 Objc 中定义的类、结构体和枚举。你可以给一个类型定义一个 `extension`,不管这个类型来自系统 framework 还是你自定义的类型。简单地导入对应的模块,用你在 Objc 中那样用相同的名字引用类,结构体和枚举。
188+
189+
例如你可以用等边三角型来扩展 `UIBezierPath` 类,基于你提供的边长和起始点创建一个简单的 Bézier 路径。
190+
191+
```
192+
// SWIFT
193+
194+
extension UIBezierPath {
195+
convenience init(triangleSideLength: Float, origin: CGPoint) {
196+
self.init()
197+
let squareRoot = Float(sqrt(3))
198+
let altitude = (squareRoot * triangleSideLength) / 2
199+
moveToPoint(origin)
200+
addLineToPoint(CGPoint(triangleSideLength, origin.x))
201+
addLineToPoint(CGPoint(triangleSideLength / 2, altitude))
202+
closePath()
203+
}
204+
}
205+
```
206+
207+
可以用 `extension` 添加属性(包括类属性,静态属性),但是这些**属性必须进行计算;`extension` 并不能给类型,结构体,枚举添加存储属性(`stored property`)。**
208+
209+
下面的例子扩展了 `CGRect` 结构体,使之拥有一个计算 `area` 的属性:
210+
211+
```
212+
// SWIFT
213+
214+
extension CGRect {
215+
var area: CGFloat {
216+
return width * height
217+
}
218+
}
219+
let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0)
220+
let area = rect.area
221+
// area: CGFloat = 500.0
222+
```
223+
224+
也可以用 `extension` 给类添加协议(`protocol conformance`)而无需继承这个类。如果是 Swift 中定义的协议,你可以将它添加到结构体或者枚举,不管这个结构体或枚举是来自 Swift 还是 Objc。
225+
226+
**不能用 `extension` 对 Objc 类型现有的方法或属性进行重载(`override`)。**
227+
228+
### Closures - 闭包
229+
230+
Objective-C blocks are automatically imported as Swift closures. For example, here is an Objective-C block variable:
231+
232+
Objc 的 `block` 被自动导入为 Swift 的闭包(`closure`)。例如下面的 Objc block 变量:
233+
234+
```
235+
// OBJECTIVE-C
236+
237+
void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) {
238+
/* ... */
239+
}
240+
```
241+
242+
在 Swift 中看起来是这样的:
243+
244+
```
245+
// SWIFT
246+
247+
let completionBlock: (NSData, NSError) -> Void = { data, error in
248+
/* ... */
249+
}
250+
```
251+
252+
Swift 的闭包和 Objc 的 block 是兼容的,所以你可以将 Swift 的闭包作为实参传给 Objc 中期望传入 block 的方法。Swift 的闭包和函数是相同的类型,所以你甚至可以传递 Swift 的函数名。
253+
254+
闭包拥有和 block 相似的捕获语义(`capture semantic`),但是有一个关键的不同之处:变量是可变的而不是拷贝一个副本。换句话说,Swift 中闭包的变量默认等同于 Objc 中用 `__block` 修饰的变量。
255+
256+
### Object Comparison - 对象比较
257+
258+
Swift 中比较两个对象有两种不同的方式。一种是相等(`equality ==`),比较两个对象的内容;另一种是全等(`identity ===`),比较两个常量或变量是否指向同一个的对象实例。
259+
260+
Swift 和 Objc 对象在 Swift 中一般用 `==``===` 操作符进行比较。Swift 为继承自 `NSObject` 类的对象提供了 `==` 操作符的默认实现,即 Swift 会调用 `NSObject` 类定义的 `isEqual:` 方法。`NSObject` 类只会判断是否全等(`identity comparison`,即是否指向同一实例),所以你应该自己实现 `NSObject` 子类的 `isEqual:` 方法。由于你可以传递 Swift 对心爱难过(包括那些没有继承自 `NSObject` 类的类对象)给 Objc API,你应该实现 `isEqual:` 方法,以便 Objc API 可以判断两个对象的内容是否相同,而不是判断是否指向同一个实例。
261+
262+
作为类对象判等的一部分,确保根据对象比较的规则来实现 `hash` 属性。进一步说,如果你想用你的类对象作为字典的 key,你还要实现 `Hashable` 协议的 `hashValue` 属性。
263+
264+
### Swift Type Compatibility - Swift 类型兼容性
265+
266+
当你在 Swift 中定义一个类继承自 `NSObject` 类或其他任意 Objc 类时,这个类自动与 Objc 兼容。这些步骤由 Swift 编译器为你完成。如果你不打算将一个 Swift 类导入到 Objc,那么你不用担心类型兼容相关的问题。另外,如果你的 Swift 类没有继承自 Objc 的类,并且你将会在 Objc 代码中使用,那么可以用 `@objc` 来修饰它。
267+
268+
`@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`
269+
270+
当你在 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`
271+
272+
Swift 也提供一个 `@objc` 的变型类允许你指定 Objc 的符号名(`symbol`)。例如,如果你的 Swift 类名含有 Objc 不支持的字符,你可以指定一个替代名以便在 Objc 中使用。如果你想为 Swift 函数提供一个 Objc 名称,应该使用 Objc 的 `selector` 语法,要记得为 `selector` 的每一部分加上分号(`:`)。
273+
274+
```
275+
// SWIFT
276+
277+
@objc(Squirrel)
278+
class Белка {
279+
@objc(initWithName:)
280+
init (имя: String) { /*...*/ }
281+
@objc(hideNuts:inTree:)
282+
func прячьОрехи(Int, вДереве: Дерево) { /*...*/ }
283+
}
284+
```
285+
286+
当你给 Swift 类使用 `@objc(<#name#>)` 时,这个类可以在 Objc 中使用,并且不需要任何命名空间。因此 `@objc(<#name#>)` 在你迁移可存档的(`archivable`) Objc 类到 Swift 时很有用,因为被存档的(`archived`)对象在存档中保存了它们的类名,你在 Objc 类中应该用 `@objc(<#name#>)` 来指定同样的名字,使得旧的存档可以在你新的 Swift 类中反存档(`unarchived`)。
287+
288+
### Objective-C Selectors
289+
290+
Objc 的 `selector` 是一种指向 Objc 方法名的类型。在 Swift 中,Objc 的 `selector` 相应地用 `Selector` 结构体表示。你可以用 string 字面量来构造一个 Swift 的 `Selector`,例如:`let mySelector: Selector = "tappedButton:"`。由于 string 字面量会自动转化为 `selector`,因此你可以传递一个 string 字面量给任何接受 `selector` 作为参数的方法。
291+
292+
```
293+
// SWIFT
294+
295+
import UIKit
296+
297+
class MyViewController: UIViewController {
298+
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
299+
300+
init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
301+
super.init(nibName: nibName, bundle: nibBundle)
302+
myButton.targetForAction("tappedButton:", withSender: self)
303+
}
304+
305+
func tappedButton(sender: UIButton!) {
306+
println("tapped button")
307+
}
308+
}
309+
```
310+
311+
> **注意**
312+
>
313+
> **`performSelector:` 方法和其他与方法调用(`selector-invoking`)相关的方法没有被迁移到 Swift,因为他们固有的不安全性。**
314+
315+
如果你的 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) 一节。
316+
317+
318+

0 commit comments

Comments
 (0)