没有哪种教育能及得上逆境 [登录·注册]

吕滔博客

首页 开发 运维 工具 摄影

UnsafePointer

Swift TIP memory 发布于April 22, 2015 标签: Swift

Swift 本身从设计上来说是一门非常安全的语言,在 Swift 的思想中,所有的引用或者变量的类型都是确定并且正确对应它们的实际类型的,你应当无法进行任意的类型转换,也不能直接通过指针做出一些出格的事情。这种安全性在日常的程序开发中对于避免不必要的 bug,以及迅速而且稳定地找出代码错误是非常有帮助的。但是凡事都有两面性,在高安全的同时,Swift 也相应地丧失了部分的灵活性。

现阶段想要完全抛弃 C 的一套东西还是相当困难的,特别是在很多上古级别的 C API 框架还在使用 (或者被间接使用)。开发者,尤其是偏向较底层的框架的开发者不得不面临着与 C API 打交道的时候,一个在 Swift 中不被鼓励的东西就出现了,那就是指针。为了与庞大的 C 系帝国进行合作,Swift 定义了一套指针的访问和转换方法,那就是 UnsafePointer 和它的一系列变体。对于使用 C API 时如果遇到接受内存地址作为参数,或者返回是内存地址的情况,在 Swift 里会将它们转为 UnsafePointer<Type> 的类型,比如说如果某个 API 在 C 中是这样的话:

void method(const int *num) {
    printf("%d",*num);
}

其对应的 Swift 方法应该是:

func method(num: UnsafePointer<CInt>) {
    print(num.memory)
}

我们这个 tip 所说的 UnsafePointer,就是 Swift 中专门针对指针的转换。对于其他的 C 中基础类型,在 Swift 中对应的类型都遵循统一的命名规则:在前面加上一个字母 C 并将原来的第一个字母大写:比如 int,bool 和 char 的对应类型分别是 CInt,CBool 和 CChar。在上面的 C 方法中,我们接受一个 int 的指针,转换到 Swift 里所对应的就是一个 CInt 的 UnsafePointer 类型。这里原来的 C API 中已经指明了输入的 num 指针的不可变的 (const),因此在 Swift 中我们与之对应的是 UnsafePointer 这个不可变版本。如果只是一个普通的可变指针的话,我们可以使用 UnsafeMutablePointer 来对应:

| C API         | Swift API                  |
|---------------|----------------------------|
| const Type *  | UnsafePointer<Type>        |
| Type *        | UnsafeMutablePointer<Type> |

在 C 中,对某个指针进行取值使用的是 *,而在 Swift 中我们可以使用 memory 属性来读取相应内存中存储的内容。通过传入指针地址进行方法调用的时候就都比较相似了,都是在前面加上 & 符号,C 的版本和 Swift 的版本只在申明变量的时候有所区别:

// C
int a = 123;
method(&a);   // 输出 123

// Swift
var a: CInt = 123
method(&a)    // 输出 123

遵守这些原则,使用 UnsafePointer 在 Swift 中进行 C API 的调用应该就不会有很大问题了。

另外一个重要的课题是如何在指针的内容和实际的值之间进行转换。比如我们如果由于某种原因需要涉及到直接使用 CFArray 的方法来获取数组中元素的时候,我们会用到这个方法:

func CFArrayGetValueAtIndex(theArray: CFArray!, idx: CFIndex) 
                                            -> UnsafePointer<Void>

因为 CFArray 中是可以存放任意对象的,所以这里的返回是一个任意对象的指针,相当于 C 中的 void *。这显然不是我们想要的东西。Swift 中为我们提供了一个强制转换的方法 unsafeBitCast,通过下面的代码,我们可以看到应当如何使用类似这样的 API,将一个指针强制按位转成所需类型的对象:

let arr = NSArray(object: "meow")
let str = unsafeBitCast(CFArrayGetValueAtIndex(arr, 0), CFString.self)
// str = "meow"

unsafeBitCast 会将第一个参数的内容按照第二个参数的类型进行转换,而不去关心实际是不是可行,这也正是 UnsafePointer 的不安全所在,因为我们不必遵守类型转换的检查,而拥有了在指针层面直接操作内存的机会。

其实说了这么多,Apple 将直接的指针访问冠以 Unsafe 的前缀,就是提醒我们:这些东西不安全,亲们能不用就别用了吧 (作为 Apple,另一个重要的考虑是如果避免指针的话可以减少很多系统漏洞)!在日常开发中,我们确实不太需要经常和这些东西打交道 (除了传入 NSError 指针这个历史遗留问题以外)。总之,尽可能地在高抽象层级编写代码,会是高效和正确的有力保证。无数先辈已经用血淋淋的教训告诉我们,要避免去做这样的不安全的操作,除非你确实知道你在做的是什么。

相关推荐

添加新评论

网站状态

  • 栏目分类:49个
  • 发布文章:1542篇
  • 用户评论:798条
  • 开博至今:4222天

正则速查

[abc] 匹配中括号中的单个字符,如a或b或c
[^abc] 匹配除了a、b、c等字符的其他单个字符
[a-z] 匹配一个字符范围,如a到z
[a-zA-Z] 匹配一个字符范围,如a-z 或 A-Z
^ 匹配行的开始
$ 匹配行的结束
\A 匹配一个字符串的开始
\z 匹配一个字符串的结束
. 匹配任意单个字符
\s 匹配空白字符,如空格,TAB
\S 匹配非空白字符
\d 匹配一个数字
\D 匹配非数字
\w 匹配一个字母
\W 匹配非字母
\b 匹配字符边界
(...) 引用所有括号中的内容
(a|b) a或者b
a? 零个或1个a
a* 零个或多个a
a+ 1个或多个a
a{3} 3次重复的a
a{3,} 3次或3次以上重复的a
a{3,6} 3到6次重复的a

修正符

/g 查找所有可能的匹配
/i 不区分大小写
/m 多行匹配
/s 单行匹配
/x 忽略空白模式
/e 可执行模式,PHP专有
/A 强制从目标字符串开头匹配
/D 使用$限制结尾字符,则不允许结尾有换行
/U 只匹配最近的一个字符串;不重复匹配

最新回复

  • : 感谢,我现在才知道还有快捷键这个东东!!!
  • 1: 天书啊
  • memory: 可以去官方网站找找手册了,好久不玩它了.
  • xiaoyan: 怎么解决的啊
  • 银行建设: 生产环境应该怎么搭建redis集群呢?
  • memory: 好久没折腾WIN了?不确认哟。
  • 虫虫: tomcat apache nginx能装吗
  • 咚咚: 开源的放一个吧
  • memory: 好几年不更新了。。。
  • liqitian: 不能用呀
  • 广州网站建设: 了解
  • memory: 这是测试,不建议正式环境这样使。
  • 广州网站建设: 构建redis集群时候,不要使用生产环境
  • memory: 照着大差不着的改一下就成了rewrite ^/sort/([0-...
  • mage: 按照上面设置的伪静态url是类似这种形式的 /listinfo-...
  • mage: 比如这种URL:帝国7.5伪静态如何可以做到这种形式(拼音或者英...
  • memory: 额,难住我了,哈哈。我项目中没有用过这么复杂的。