Files
the-swift-programming-langu…/source/chapter4/04_Interacting_with_C_Pointers.md
2018-05-06 19:13:01 +08:00

80 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Swift 与 C 语言指针友好合作
-----------------
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
> 校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
本页包含内容:
- [用以输入/输出的参数指针](#inout-para-pointer)
- [作为数组使用的参数指针](#array-as-para-pointer)
- [用作字符串参数的指针](#string-as-para-pointer)
- [指针参数转换的安全性](#security-of-pointer-cast)
Objective-C 和 C 的 API 常常会需要用到指针。Swift 中的数据类型都原生支持基于指针的 Cocoa API不仅如此Swift 会自动处理部分最常用的将指针作为参数传递的情况。这篇文章中,我们将着眼于在 Swift 中让 C 语言指针与变量、数组和字符串共同工作。
####用以输入/输出的参数指针
C 和 Objective-C 并不支持多返回值,所以 Cocoa API 中常常将指针作为一种在方法间传递额外数据的方式。Swift 允许指针被当作 `inout` 参数使用,所以你可以用符号 `&` 将对一个变量的引用作为指针参数传递。举例来说:`UIColor` 中的 `getRed(_:green:blue:alpha:)` 方法需要四个 `CGFloat*` 指针来接收颜色的组成信息,我们使用 `&` 来将这些组成信息捕获为本地变量:
```swift
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
```
另一种常见的情况是 Cocoa 中 `NSError` 的习惯用法。许多方法会使用一个 `NSError**` 参数来储存可能的错误的信息。举例来说:我们用 `NSFileManager``contentOfDirectoryAtPath(_:error:)` 方法来将目录下的内容列表,并将潜在的错误指向一个 `NSError?` 变量:
```swift
var maybeError: NSError?
if let contents = NSFileManager.defaultManager()
.contentsOfDirectoryAtPath("/usr/bin", error: &maybeError) {
// Work with the directory contents
} else if let error = maybeError {
// Handle the error
}
```
为了安全性Swift 要求被使用 `&` 传递的变量已经初始化。因为无法确定这个方法会不会在写入数据前尝试从指针中读取数据。
####作为数组使用的参数指针
在 C 语言中,数组和指针的联系十分紧密,而 Swift 允许数组能够作为指针使用,从而与基于数组的 C 语言 API 协同工作更加简单。一个固定的数组可以使用一个常量指针直接传递,一个变化的数组可以用 `&` 运算符将一个非常量指针传递。就和输入/输出参数指针一样。举例来说:我们可以用 Accelerate 框架中的 `vDSP_vadd` 方法让两个数组 `a``b` 相加,并将结果写入第三个数组 `result`
```swift
import Accelerate
let a: [Float] = [1, 2, 3, 4]
let b: [Float] = [0.5, 0.25, 0.125, 0.0625]
var result: [Float] = [0, 0, 0, 0]
vDSP_vadd(a, 1, b, 1, &result, 1, 4)
// result now contains [1.5, 2.25, 3.125, 4.0625]
```
## 用作字符串参数的指针
C 语言中用 `cont char*` 指针来作为传递字符串的基本方式。Swift 中的 `String` 可以被当作一个无限长度 UTF-8编码的 `const char*` 指针来传递给方法。举例来说:我们可以直接传递一个字符串给一个标准 C 和 POSIX 库方法
```swift
puts("Hello from libc")
let fd = open("/tmp/scratch.txt", O_WRONLY|O_CREAT, 0o666)
if fd < 0 {
perror("could not open /tmp/scratch.txt")
} else {
let text = "Hello World"
write(fd, text, strlen(text))
close(fd)
}
```
## 指针参数转换的安全性
Swift 很努力地使与 C 语言指针的交互更加便利,因为它们广泛地存在于 Cocoa 之中,同时保持一定的安全性。然而,相比你的其他 Swift 代码与 C 语言的指针交互具有潜在的不安全性,所以务必要小心使用。其中特别要注意:
- 如果被调用者为了在其返回值之后再次使用而保存了 C 指针的数据,那么这些转换使用起来并不安全。转换后的指针仅在调用期间保证有效。甚至你将同样的变量、数组或字符串作为多指针参数再次传递,你每次都会收到一个不同的指针。这个异常将全局或静态地储存为变量。你可以安全地将这段地址当作永久唯一的指针使用。例如:作为一个 KVO 上下文参数使用的时候。
- 当指针类型为 `Array``String` 时,溢出检查不是强制进行的。 基于 C 语言的 API 无法增加数组和字符串大小,所以在你将其传递到基于 C 语言的 API 之前,你必须确保数组或字符的大小正确。
如果你需要使用基于指针的 API 时没有遵守以上指导,或是你重写了接受指针参数的 Cocoa 方法,于是你可以在 Swift 中直接用不安全的指针来使用未经处理的内存。在未来的文章中我们将着眼于更加高级的情况。