Swift与C指针交互
Objective-C和C API经常须要使用指针. 在设计上, Swift数据类型能够自然的与基于指针的Cocoa API一起工作, Swift自己主动处理几种经常使用的指针參数. 在本文中, 我们将看到C中的指针參数怎样与Swift中的变量,数组,字符串一起工作.
指针作为输入/输出參数
C和Objective-C不支持多个返回值, 所以Cocoa API常常使用指针传递附加參数到函数. Swift同意把指针參数看成[inout]參数, 所以你能够用相同的&语法传递一个变量的引用作为指针. 比如: UIColor的getRed(_:green:blue:alpha:)方法使用4个CGFloat*指针来接受颜色的组合. 我们能够用&来得到这些值:
var r: CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0 color.getRed(&r, green: &g, blue: &b, alpha: &a)
还有一个常常使用的是NSError. 很多方法使用了NSError*參数来保存发生的错误. 比如: 我们列举文件夹里的内容使用NSFileManager的contentsOfDirectoryAtPath(_:error:)方法, 直接使用NSError?变量来保存可能的错误:
var maybeError:NSError? if let contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/usr/bin", error: &maybeError){ //内容处理 for i in contents{ println(i) } }else if let error = maybeError{ //错误处理 println(error.description) }
安全起见, Swift要求变量在使用&前须要初始化. 由于它不知道被调用的方法是否在改动它之前会读取指针
数组指针
在C中数组与指针紧紧相连. 为方便使用基于数组的C API, Swift同意将Array作为指针. 不可改动数组能够直接当成常量指针, 可改动数组能够使用&操作符做为很量指针(就和inout參数一样). 比如: 我们把两个数组 a 和 b 使用vDSP_vadd函数(Accelerte framework)相加, 把结果写到第三个数组 result 中:
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使用 const char*指针作为传递字符串的主要方式. Swift String能够作为const char*指针, 它会给函数传递一个 null结束, UTF-8编码的字符串指针. 比如, 我们能够直接给标准C和POSIX库函数传递字符串:
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指针的交互方便并提供了一定的安全性, 由于C指针无处不在. 可是, 与C指针交互相对于Swift代码来说还是内存的不安全, 因此须要特别注意. 特别的:
* These conversions cannot safely be used if the callee saves the pointer value for use after it returns. The pointer that results from these conversions is only guaranteed to be valid for the duration of a call. Even if you pass the same variable, array, or
string as multiple pointer arguments, you could receive a different pointer each time. An exception to this is global or static stored variables. You can safely use the address of a global variable as a persistent unique pointer value, e.g.: as a KVO context
parameter.
* Array和String指针没有边界检查. C API不会扩大数组和字符串, 因此在调用前你须要为它分配足够的大小
假设上面的指南不满足你指针交互的须要, 或者你想手动控制指针參数, 你能够直接使用unsafe指针操作内存. 我们会在将来的文章中看到很多其它高级应用.