- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Swift中的指针操作和使用详细介绍由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
Apple期望在Swift中指针能够尽量减少登场几率,因此在Swift中指针被映射为了一个泛型类型,并且还比较抽象。这在一定程度上造成了在Swift中指针使用的困难,特别是对那些并不熟悉指针,也没有多少指针操作经验的开发者(包括我自己也是)来说,在Swift中使用指针确实是一个挑战。在这篇文章里,我希望能从最基本的使用开始,总结一下在Swift中使用指针的一些常见方式和场景。这篇文章假定你至少知道指针是什么,如果对指针本身的概念不太清楚的话,可以先看看这篇五分钟C指针教程(或者它的中文版本),应该会很有帮助.
初步 。
在Swift中,指针都使用一个特殊的类型来表示,那就是UnsafePointer<T>。遵循了Cocoa的一贯不可变原则,UnsafePointer<T> 也是不可变的。当然对应地,它还有一个可变变体,UnsafeMutablePointer<T>。绝大部分时间里,C中的指针都会被以这两种类型引入到Swift中:C中const修饰的指针对应UnsafePointer(最常见的应该就是C字符串的const char *了),而其他可变的指针则对应UnsafeMutablePointer。除此之外,Swift中存在表示一组连续数据指针的UnsafeBufferPointer<T>,表示非完整结构的不透明指针COpaquePointer等等。另外你可能已经注意到了,能够确定指向内容的指针类型都是泛型的struct,我们可以通过这个泛型来对指针指向的类型进行约束以提供一定安全性.
对于一个UnsafePointer<T>类型,我们可以通过memory属性对其进行取值,如果这个指针是可变的UnsafeMutablePointer<T> 类型,我们还可以通过memory对它进行赋值。比如我们想要写一个利用指针直接操作内存的计数器的话,可以这么做:
。
这里和C的指针使用类似,我们通过在变量名前面加上&符号就可以将指向这个变量的指针传递到接受指针作为参数的方法中去。在上面的incrementor中我们通过直接操作memory属性改变了指针指向的内容.
与这种做法类似的是使用Swift的inout关键字。我们在将变量传入inout参数的函数时,同样也使用&符号表示地址。不过区别是在函数体内部我们不需要处理指针类型,而是可以对参数直接进行操作.
。
。
虽然&在参数传递时表示的意义和C中一样,是某个“变量的地址”,但是在Swift中我们没有办法直接通过这个符号获取一个UnsafePointer的实例。需要注意这一点和C有所不同:
。
。
指针初始化和内存管理 。
在Swift中不能直接取到现有对象的地址,我们还是可以创建新的UnsafeMutablePointer对象。与Swift 中其他对象的自动内存管理不同,对于指针的管理,是需要我们手动进行内存的申请和释放的。一个 UnsafeMutablePointer的内存有三种可能状态:
1.内存没有被分配,这意味着这是一个 null 指针,或者是之前已经释放过; 2.内存进行了分配,但是值还没有被初始化; 3.内存进行了分配,并且值已经被初始化.
其中只有第三种状态下的指针是可以保证正常使用的。UnsafeMutablePointer的初始化方法(init)完成的都是从其他类型转换到UnsafeMutablePointer的工作。我们如果想要新建一个指针,需要做的是使用alloc:这个类方法。该方法接受一个num: Int作为参数,将向系统申请num个数的对应泛型类型的内存。下面的代码申请了一个Int大小的内存,并返回指向这块内存的指针:
接下来应该做的是对这个指针的内容进行初始化,我们可以使用initialize:方法来完成初始化:
在完成初始化后,我们就可以通过memory来操作指针指向的内存值了.
。
在使用之后,我们最好尽快释放指针指向的内容和指针本身。与initialize:配对使用的destroy用来销毁指针指向的对象,而与alloc:对应的dealloc:用来释放之前申请的内存。它们都应该被配对使用:
。
注意:其实在这里对于Int这样的在C中映射为int的“平凡值”来说,destroy并不是必要的,因为这些值被分配在常量段上。但是对于像类的对象或者结构体实例来说,如果不保证初始化和摧毁配对的话,是会出现内存泄露的。所以没有特殊考虑的话,不论内存中到底是什么,保证initialize:和destroy配对会是一个好习惯.
指向数组的指针 。
在Swift中将一个数组作为参数传递到C API时,Swift已经帮助我们完成了转换,这在Apple的官方博客中有个很好的例子:
。
。
对于一般的接受const数组的C API,其要求的类型为UnsafePointer,而非const的数组则对应UnsafeMutablePointer。使用时,对于const的参数,我们直接将Swift数组传入(上例中的a和b);而对于可变的数组,在前面加上&后传入即可(上例中的result).
对于传参,Swift进行了简化,使用起来非常方便。但是如果我们想要使用指针来像之前用memory的方式直接操作数组的话,就需要借助一个特殊的类型:UnsafeMutableBufferPointer.
Buffer Pointer是一段连续的内存的指针,通常用来表达像是数组或者字典这样的集合类型.
。
。
指针操作和转换 。
withUnsafePointer 。
上面我们说过,在Swift中不能像C里那样使用&符号直接获取地址来进行操作。如果我们想对某个变量进行指针操作,我们可以借助withUnsafePointer这个辅助方法。这个方法接受两个参数,第一个是 inout的任意类型,第二个是一个闭包。Swift会将第一个输入转换为指针,然后将这个转换后的Unsafe的指针作为参数,去调用闭包。使用起来大概是这个样子:
这里其实我们做了和文章一开始的incrementor相同的事情,区别在于不需要通过方法的调用来将值转换为指针。这么做的好处对于那些只会执行一次的指针操作来说是显而易见的,可以将“我们就是想对这个指针做点事儿”这个意图表达得更加清晰明确.
。
unsafeBitCast 。
unsafeBitCast是非常危险的操作,它会将一个指针指向的内存强制按位转换为目标的类型。因为这种转换是在Swift的类型管理之外进行的,因此编译器无法确保得到的类型是否确实正确,你必须明确地知道你在做什么。比如:
因为NSArray是可以存放任意NSObject对象的,当我们在使用CFArrayGetValueAtIndex从中取值的时候,得到的结果将是一个UnsafePointer<Void>。由于我们很明白其中存放的是String对象,因此可以直接将其强制转换为CFString.
。
关于unsafeBitCast一种更常见的使用场景是不同类型的指针之间进行转换。因为指针本身所占用的的大小是一定的,所以指针的类型进行转换是不会出什么致命问题的。这在与一些C API协作时会很常见。比如有很多C API要求的输入是void *,对应到Swift中为UnsafePointer<Void>。我们可以通过下面这样的方式将任意指针转换为UnsafePointer.
。
总结 。
Swift从设计上来说就是以安全作为重要原则的,虽然可能有些啰嗦,但是还是要重申在Swift中直接使用和操作指针应该作为最后的手段,它们始终是无法确保安全的。从传统的C代码和与之无缝配合的Objective-C代码迁移到Swift并不是一件小工程,我们的代码库肯定会时不时出现一些和C协作的地方。我们当然可以选择使用Swift重写部分陈旧代码,但是对于像是安全或者性能至关重要的部分,我们可能除了继续使用C API以外别无选择。如果我们想要继续使用那些API的话,了解一些基本的Swift指针操作和使用的知识会很有帮助.
对于新的代码,尽量避免使用Unsafe开头的类型,意味着可以避免很多不必要的麻烦。Swift给开发者带来的最大好处是可以让我们用更加先进的编程思想,进行更快和更专注的开发。只有在尊重这种思想的前提下,我们才能更好地享受这门新语言带来的种种优势。显然,这种思想是不包括到处使用 UnsafePointer的.
最后此篇关于Swift中的指针操作和使用详细介绍的文章就讲到这里了,如果你想了解更多关于Swift中的指针操作和使用详细介绍的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有没有办法在 .swift 文件(编译成 .swift 模块)中声明函数,如下所示: 你好.swift func hello_world() { println("hello world")
我正在尝试使用 xmpp_messenger_ios 和 XMPPFramework 在 iOS 上执行 MUC 这是加入房间的代码。 func createOrJoinRoomOnXMPP()
我想在我的应用程序上创建一个 3D Touch 快捷方式,我已经完成了有关快捷方式本身的所有操作,它显示正确,带有文本和图标。 当我运行这个快捷方式时,我的应用程序崩溃了,因为 AppDelegate
我的代码如下: let assetTag = Expression("asset_tag") let query2 = mdm.select(mdm[assetTag],os, mac, lastRe
我的 swift 代码如下所示 Family.arrayTuple:[(String,String)]? = [] Family.arrayTupleStorage:String? Family.ar
这是我的 JSON,当我读取 ord 和 uniq 数据时出现错误 let response2 : [String: Any] = ["Response":["status":"SUCCESS","
我想将 swift 扩展文件移动到 swift 包中。但是,将文件移动到 swift 包后,我遇到了这种错误: "Type 'NSAttributedString' has no member 'ma
使用CocoaPods,我们可以设置以下配置: pod 'SourceModel', :configurations => ['Debug'] 有什么方法可以用 Swift Package Manag
我正在 Xcode 中开发一个 swift 项目。我将其称为主要项目。我大部分都在工作。我在日期选择器、日期范围和日期数学方面遇到了麻烦,因此我开始了另一个名为 StarEndDate 的项目,其中只
这是 ObjectiveC 代码: CCSprite *progress = [CCSprite spriteWithImageNamed:@"progress.png"]; mProgressBar
我正在创建一个命令行工具,在 Xcode 中使用 Swift。我想使用一个类似于 grunt 的配置文件确实如此,但我希望它是像 Swift 包管理器的 package.swift 文件那样的快速代码
我假设这意味着使用系统上安装的任何 swift 运行脚本:#!/usr/bin/swift 如何指定脚本适用的解释器版本? 最佳答案 Cato可用于此: #!/usr/bin/env cato 1.2
代码说完全没问题,没有错误,但是当我去运行模拟器的时候,会出现这样的字样: (Swift.LazyMapCollection (_base:[ ] 我正在尝试创建一个显示报价的报价应用。 这是导入
是否可以在运行 Swift(例如 Perfect、Vapor、Kitura 等)的服务器上使用 RealmSwift 并使用它来存储数据? (我正在考虑尝试将其作为另一种解决方案的替代方案,例如 no
我刚开始学习编程,正在尝试完成 Swift 编程书中的实验。 它要求““编写一个函数,通过比较两个 Rank 值的原始值来比较它们。” enum Rank: Int { case Ace = 1 ca
在您将此问题标记为重复之前,我检查了 this question 它对我不起作用。 如何修复这个错误: error: SWIFT_VERSION '5.0' is unsupported, suppo
从 Xcode 9.3 开始,我在我的模型中使用“Swift.ImplicitlyUnwrappedOptional.some”包裹了我的字符串变量 我不知道这是怎么发生的,但它毁了我的应用程序! 我
这个问题在这里已经有了答案: How to include .swift file from other .swift file in an immediate mode? (2 个答案) 关闭 6
我正在使用 Swift Package Manager 创建一个应用程序,我需要知道构建项目的配置,即 Debug 或 Release。我试图避免使用 .xcodeproj 文件。请有人让我知道这是否
有一个带有函数定义的文件bar.swift: func bar() { println("bar") } 以及一个以立即模式运行的脚本foo.swift: #!/usr/bin/xcrun s
我是一名优秀的程序员,十分优秀!