阅读更多

0顶
0踩

编程语言

转载新闻 Swift中的指针操作及使用

2015-01-21 11:06 by 副主编 mengyidan1988 评论(0) 有4591人浏览
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对它进行赋值。比如我们想要写一个利用指针直接操作内存的计数器的话,可以这么做:
func incrementor(ptr: UnsafeMutablePointer<Int>) {
    ptr.memory += 1
}
var a = 10
incrementor(&a)
a  // 11

这里和C的指针使用类似,我们通过在变量名前面加上&符号就可以将指向这个变量的指针传递到接受指针作为参数的方法中去。在上面的incrementor中我们通过直接操作memory属性改变了指针指向的内容。

与这种做法类似的是使用Swift的inout关键字。我们在将变量传入inout参数的函数时,同样也使用&符号表示地址。不过区别是在函数体内部我们不需要处理指针类型,而是可以对参数直接进行操作。
func incrementor1(inout num: Int) {
    num += 1
}
var b = 10
incrementor1(&b)
b  // 11

虽然&在参数传递时表示的意义和C中一样,是某个“变量的地址”,但是在Swift中我们没有办法直接通过这个符号获取一个UnsafePointer的实例。需要注意这一点和C有所不同:
// 无法编译
let a = 100
let b = &a

指针初始化和内存管理

在Swift中不能直接取到现有对象的地址,我们还是可以创建新的UnsafeMutablePointer对象。与Swift 中其他对象的自动内存管理不同,对于指针的管理,是需要我们手动进行内存的申请和释放的。一个 UnsafeMutablePointer的内存有三种可能状态:
  • 内存没有被分配,这意味着这是一个 null 指针,或者是之前已经释放过;
  • 内存进行了分配,但是值还没有被初始化;
  • 内存进行了分配,并且值已经被初始化。

其中只有第三种状态下的指针是可以保证正常使用的。UnsafeMutablePointer的初始化方法(init)完成的都是从其他类型转换到UnsafeMutablePointer的工作。我们如果想要新建一个指针,需要做的是使用alloc:这个类方法。该方法接受一个num: Int作为参数,将向系统申请num个数的对应泛型类型的内存。下面的代码申请了一个Int大小的内存,并返回指向这块内存的指针:
var intPtr = UnsafeMutablePointer<Int>.alloc(1)
// "UnsafeMutablePointer(0x7FD3A8E00060)"

接下来应该做的是对这个指针的内容进行初始化,我们可以使用initialize:方法来完成初始化:
intPtr.initialize(10)
// intPtr.memory 为 10

在完成初始化后,我们就可以通过memory来操作指针指向的内存值了。

在使用之后,我们最好尽快释放指针指向的内容和指针本身。与initialize:配对使用的destroy用来销毁指针指向的对象,而与alloc:对应的dealloc:用来释放之前申请的内存。它们都应该被配对使用:
intPtr.destroy()
intPtr.dealloc(1)
intPtr = nil

引用
注意:其实在这里对于Int这样的在C中映射为int的“平凡值”来说,destroy并不是必要的,因为这些值被分配在常量段上。但是对于像类的对象或者结构体实例来说,如果不保证初始化和摧毁配对的话,是会出现内存泄露的。所以没有特殊考虑的话,不论内存中到底是什么,保证initialize:和destroy配对会是一个好习惯。

指向数组的指针

在Swift中将一个数组作为参数传递到C API时,Swift已经帮助我们完成了转换,这在Apple的官方博客中有个很好的例子:
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]

对于一般的接受const数组的C API,其要求的类型为UnsafePointer,而非const的数组则对应UnsafeMutablePointer。使用时,对于const的参数,我们直接将Swift数组传入(上例中的a和b);而对于可变的数组,在前面加上&后传入即可(上例中的result)。

对于传参,Swift进行了简化,使用起来非常方便。但是如果我们想要使用指针来像之前用memory的方式直接操作数组的话,就需要借助一个特殊的类型:UnsafeMutableBufferPointer。Buffer Pointer是一段连续的内存的指针,通常用来表达像是数组或者字典这样的集合类型。
var array = [1, 2, 3, 4, 5]
var arrayPtr = UnsafeMutableBufferPointer<Int>(start: &array, count: array.count)
// baseAddress 是第一个元素的指针
var basePtr = arrayPtr.baseAddress as UnsafeMutablePointer<Int>
basePtr.memory // 1
basePtr.memory = 10
basePtr.memory // 10
//下一个元素
var nextPtr = basePtr.successor()
nextPtr.memory // 2

指针操作和转换
  • withUnsafePointer

上面我们说过,在Swift中不能像C里那样使用&符号直接获取地址来进行操作。如果我们想对某个变量进行指针操作,我们可以借助withUnsafePointer这个辅助方法。这个方法接受两个参数,第一个是 inout的任意类型,第二个是一个闭包。Swift会将第一个输入转换为指针,然后将这个转换后的Unsafe的指针作为参数,去调用闭包。使用起来大概是这个样子:
var test = 10
test = withUnsafeMutablePointer(&test, { (ptr: UnsafeMutablePointer<Int>) -> Int in
    ptr.memory += 1
    return ptr.memory
})
test // 11

这里其实我们做了和文章一开始的incrementor相同的事情,区别在于不需要通过方法的调用来将值转换为指针。这么做的好处对于那些只会执行一次的指针操作来说是显而易见的,可以将“我们就是想对这个指针做点事儿”这个意图表达得更加清晰明确。
  • unsafeBitCast

unsafeBitCast是非常危险的操作,它会将一个指针指向的内存强制按位转换为目标的类型。因为这种转换是在Swift的类型管理之外进行的,因此编译器无法确保得到的类型是否确实正确,你必须明确地知道你在做什么。比如:
let arr = NSArray(object: "meow")
let str = unsafeBitCast(CFArrayGetValueAtIndex(arr, 0), CFString.self)
str // “meow”

因为NSArray是可以存放任意NSObject对象的,当我们在使用CFArrayGetValueAtIndex从中取值的时候,得到的结果将是一个UnsafePointer<Void>。由于我们很明白其中存放的是String对象,因此可以直接将其强制转换为CFString。

关于unsafeBitCast一种更常见的使用场景是不同类型的指针之间进行转换。因为指针本身所占用的的大小是一定的,所以指针的类型进行转换是不会出什么致命问题的。这在与一些C API协作时会很常见。比如有很多C API要求的输入是void *,对应到Swift中为UnsafePointer<Void>。我们可以通过下面这样的方式将任意指针转换为UnsafePointer。
var count = 100
var voidPtr = withUnsafePointer(&count, { (a: UnsafePointer<Int>) -> UnsafePointer<Void> in
    return unsafeBitCast(a, UnsafePointer<Void>.self)
})
// voidPtr 是 UnsafePointer<Void>。相当于 C 中的 void *
// 转换回 UnsafePointer<Int>
var intPtr = unsafeBitCast(voidPtr, UnsafePointer<Int>.self)
intPtr.memory //100

总结

Swift从设计上来说就是以安全作为重要原则的,虽然可能有些啰嗦,但是还是要重申在Swift中直接使用和操作指针应该作为最后的手段,它们始终是无法确保安全的。从传统的C代码和与之无缝配合的Objective-C代码迁移到Swift并不是一件小工程,我们的代码库肯定会时不时出现一些和C协作的地方。我们当然可以选择使用Swift重写部分陈旧代码,但是对于像是安全或者性能至关重要的部分,我们可能除了继续使用C API以外别无选择。如果我们想要继续使用那些API的话,了解一些基本的Swift指针操作和使用的知识会很有帮助。

对于新的代码,尽量避免使用Unsafe开头的类型,意味着可以避免很多不必要的麻烦。Swift给开发者带来的最大好处是可以让我们用更加先进的编程思想,进行更快和更专注的开发。只有在尊重这种思想的前提下,我们才能更好地享受这门新语言带来的种种优势。显然,这种思想是不包括到处使用 UnsafePointer的。
作者:王巍(@onevcat),iOS和Unity3D开发者。
来自: OneV's Den
0
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • Swift中的指针操作和使用详细介绍

    主要介绍了Swift中的指针操作和使用详细介绍,Apple期望在Swift中指针能够尽量减少登场几率,因此在Swift中指针被映射为了一个泛型类型,并且还比较抽象,本文详细讲解了Swift中指针的相关知识,需要的朋友可以参考下

  • Swift中的指针操作详解

    从传统的C代码和与之无缝配合的Objective-C代码迁移到Swift并非小工程,我们的代码库肯定会时不时出现一些和C协作的地方,如果想要继续使用那些C API的话,了解一些基本的Swift指针操作和使用的知识会很有帮助。...

  • Swift 中如何使用指针?

    Swift 中指针被映射为了一个泛型类型,苹果希望我们在Swift开发中尽量减少指针的使用。指针类型都是泛型的 struct,开发者可以通过这个泛型来对指针指向的类型进行约束以提供一定安全性。 简介 UnsafePointer: 不...

  • Swift —— 指针

    Swift —— 指针1.指针2. 指针类型3. 原始指针的使用4. 泛型指针的使用5. 指针读取macho中的属性名称6. 内存绑定7. 内存管理 1.指针 为什么说指针是不安全的呢?主要以下几点: 比如我们在创建一个对象的时候,是...

  • c语言中指针怎么输入,在Swift中怎么使用C语言的指针

    在Swift中怎么使用C语言的指针Objective-C和C语言经常需要使用到指针。Swift中的数据类型由于良好的设计,使其可以和基于指针的C语言API无缝混用。同时Swift也可以自动处理大多数将指针作为参数的情况。在这篇文章里...

  • swift指针&内存管理-指针类型使用

    为什么说指针不安全原因:堆内存生命周期,内存空间访问越界,指针类型与内存类型不一致; Swift中的指针分为两类 typed pointer & raw pointer 原始指针-rawPointer 的使用 泛型指针的使用

  • Swift 指针 & 内存管理

    Swift 指针 & 内存管理

  • swift 生命周期_Swift 中的指针

    题图来自互联网指针是 C / C++ 中一个很重要的概念,是这些相对低级的语言的灵魂,然而 Swift 似乎天生对指针十分不友好,繁琐的用法让很多初学者一上来十分摸不着头脑。本文就简单谈谈 Swift 中指针的一些用法。为...

  • Swift 中的指针使用

    这在一定程度上造成了在 Swift 中指针使用的困难,特别是对那些并不熟悉指针,也没有多少指针操作经验的开发者 (包括我自己也是) 来说,在 Swift 中使用指针确实是一个挑战。在这篇文章里,我希望能从最基本的使用...

  • Swift指针的应用

    Swift与指针 由于Swift本身是一门较为现代的语言,支持很多高级特性,所以...所以,Swift通过在施加某种限制的前提下为开发者暴露了指针的使用接口,本篇文章重点介绍Swift使用指针的相关类型、函数,以及在实践应用

  • swift指针的操作

    这个人贼懒,但是还想方便以后查看,请点这个链接 https://www.jianshu.com/p/e90393ba2aea

  • [Swift]Swift 中的指针使用

    Apple 期望在 Swift 中指针能够尽量减少登场几率,因此...这在一定程度上造成了在 Swift 中指针使用的困难,特别是对那些并不熟悉指针,也没有多少指针操作经验的开发者 (包括我自己也是) 来说,在 Swift 中使用指

  • Swift 2中的指针与Objective-C指针相互传递

    在桥接文件中通常使用的OC代码,在OC中就可以直接操作调用C的函数。1. 在Swift中读C指针下面桥接文件中的方法会返回一个int指针,即C术语里面的(int *):@interface PointerBridge : NSObject { int count; } - ...

  • Swift 中的指针操作

    http://www.swiftyper.com/2017/01/15/unsafe-swift/ ...但是,Swift 也提供了我们使用指针直接操作内存的方法,直接操作内存是很危险的行为,很容易就出现错误,因此官方将直接操作内存称为 “unsafe 特性”。 一旦我

  • c语言指针换字母,Swift中C语言指针的访问和转换方法

    Swift 本身从设计上来说是一门非常安全的语言,在 Swift 的思想中,所有的引用或者变量的类型都是确定并且正确对应它们的实际类型的,你应当无法进行任意的类型转换,也不能直接通过指针做出一些出格的事情。...

  • spring-ai-spring-boot-autoconfigure-1.0.0-M5.jar中文文档.zip

    # 【spring-ai-spring-boot-autoconfigure-***.jar中文文档.zip】 中包含: 中文文档:【spring-ai-spring-boot-autoconfigure-***-javadoc-API文档-中文(简体)版.zip】 jar包下载地址:【spring-ai-spring-boot-autoconfigure-***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【spring-ai-spring-boot-autoconfigure-***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【spring-ai-spring-boot-autoconfigure-***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【spring-ai-spring-boot-autoconfigure-***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: spring-ai-spring-boot-autoconfigure-***.jar中文文档.zip,java,spring-ai-spring-boot-autoconfigure-***.jar,org.springframework.ai,spring-ai-spring-boot-autoconfigure,***,org.springframework.ai.autoconfigure.anthropic,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,springframework,spring,ai,boot,autoconfigure,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【spring-ai-spring-boot-autoconfigure-***.jar中文文档.zip】,再解压其中的 【spring-ai-spring-boot-autoconfigure-***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件; # Maven依赖: ``` <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-spring-boot-autoconfigure</artifactId> <version>***</version> </dependency> ``` # Gradle依赖: ``` Gradle: implementation group: 'org.springframework.ai', name: 'spring-ai-spring-boot-autoconfigure', version: '***' Gradle (Short): implementation 'org.springframework.ai:spring-ai-spring-boot-autoconfigure:***' Gradle (Kotlin): implementation("org.springframework.ai:spring-ai-spring-boot-autoconfigure:***") ``` # 含有的 Java package(包): ``` org.springframework.ai.autoconfigure.anthropic org.springframework.ai.autoconfigure.azure.openai org.springframework.ai.autoconfigure.bedrock org.springframework.ai.autoconfigure.bedrock.anthropic org.springframework.ai.autoconfigure.bedrock.anthropic3

  • 50页-道路环卫保洁服务项目管理计划方案.pdf

    在当今智慧城市的建设浪潮中,智慧环卫作为城市管理的重要组成部分,正以其独特的魅力引领着环卫行业的变革。本方案旨在通过一系列高科技手段,如物联网、大数据、云计算等,全面提升环卫作业效率与管理水平,为城市居民创造更加清洁、宜居的生活环境。 一、智慧环卫系统概述与核心亮点 智慧环卫系统是一个集机械化保洁、垃圾清运、设施管理、事件指挥调度等多功能于一体的综合性管理平台。其核心亮点在于通过高精度定位、实时监控与智能分析,实现环卫作业的精细化管理。例如,机械化保洁管理子系统能够实时监控机扫车、洒水车等作业车辆的运行状态,自动规划最优作业路线,并根据作业完成情况生成考核评价报表,极大地提高了作业效率与服务质量。同时,垃圾清运管理子系统则通过安装GPS定位设备和油量传感器,对清运车辆进行全方位监控,确保垃圾清运过程的规范与高效,有效解决了城市垃圾堆积与随意倾倒的问题。此外,系统还配备了垃圾箱满溢报警系统,通过智能感应技术,当垃圾箱内垃圾达到预设高度时自动报警,提醒作业人员及时清运,避免了因垃圾满溢而引发的居民投诉与环境污染。 二、智慧环卫系统的趣味性与知识性融合 智慧环卫系统不仅实用性强,还蕴含着丰富的趣味性与知识性。以餐厨垃圾收运管理子系统为例,该系统通过为餐厨垃圾收运车辆安装GPS定位、车载称重、视频监控等多种感知设备,实现了对餐厨垃圾收运过程的全程监控与智能管理。作业人员可以通过手机APP实时查看车辆位置、行驶轨迹及收运情况,仿佛在玩一场现实版的“垃圾追踪游戏”。同时,系统还能自动生成餐厨垃圾收运统计报表,帮助管理人员轻松掌握收运量、违规情况等关键数据,让数据管理变得既科学又有趣。此外,中转站视频监控子系统更是将趣味性与实用性完美结合,通过高清摄像头与双向语音对讲功能,实现了对中转站内外环境的实时监控与远程指挥,让管理人员足不出户就能掌控全局,仿佛拥有了一双“千里眼”和一对“顺风耳”。 三、智慧环卫系统的未来展望与社会价值 随着科技的不断进步与智慧城市建设的深入推进,智慧环卫系统将迎来更加广阔的发展前景。未来,智慧环卫系统将更加注重数据的深度挖掘与分析,通过大数据与人工智能技术,为城市环卫管理提供更加精准、高效的决策支持。同时,系统还将加强与其他城市管理系统的互联互通,实现资源共享与协同作战,共同推动城市管理的智能化、精细化水平。从社会价值来看,智慧环卫系统的推广与应用将有效提升城市环境卫生质量,改善居民生活环境,提升城市形象与竞争力。此外,系统还能通过优化作业流程、减少资源浪费等方式,为城市可持续发展贡献重要力量。可以说,智慧环卫系统不仅是城市管理的得力助手,更是推动社会进步与文明发展的重要力量。

  • 微信小程序驾校管理平台约车小程序demo完整源码下载-完整源码.zip

    微信小程序驾校管理平台约车小程序demo完整源码下载_完整源码

  • MATLAB实现含风电不确定性的电力系统低碳调度模型

    内容概要:本文详细介绍了使用MATLAB和YALMIP工具包构建的电力系统低碳调度模型。该模型主要解决风电和负荷不确定性带来的挑战,采用模糊机会约束处理风电预测误差,将复杂的非线性约束转化为混合整数线性规划问题。文中展示了如何通过分段线性化、大M法等技巧提高求解效率,并实现了包括火电、水电、风电、储能等多种能源类型的综合调度。此外,还讨论了碳排放成本、启停时间约束、爬坡率约束以及储能系统的建模方法。最终,通过结果可视化展示各成本构成及其对调度策略的影响。 适合人群:从事电力系统优化研究的专业人士,尤其是熟悉MATLAB编程并希望深入了解低碳调度模型的研究人员和技术人员。 使用场景及目标:适用于需要处理风电不确定性、优化电力系统调度的研究项目。目标是降低电力生产成本的同时减少碳排放,确保电力系统的稳定性和经济性。 其他说明:代码中包含了详细的注释和扩展提示,方便进一步修改与应用。对于大规模电力系统调度问题,提供了高效的求解策略和性能优化建议。

Global site tag (gtag.js) - Google Analytics