关于Swift中的指针的那些事

栏目: C · 发布时间: 5年前

内容简介:在

前言

Objective-c 的世界中,一切对象都是指针。它是一种运行时语言,具体指针的对象类型将会在运行时,由系统分配。这样虽然自由,但是却并不安全。

Swift 世界就不一样了, Swift 的世界很安全(至少大部分时候情况如此)。我们不必为对象运行时的类型担忧,这是 Swift 为我们构筑的一层堡垒。但是在一些时候,这层堡垒也成为束缚我们行为的操作。

正文

Swift 也为操作指针这种不安全行为提供了支持。

MemoryLayout

MemoryLayout 是Swift 为结构体 struct 等一些需要获取具体内存空间大小定义的枚举。

它包含一系列方法,可以使我们更方便的使用。

主要属性就是三个:

/// 对应类型的实例在内存占用的真实字节大小
public static var size: Int { get }
/// 对应类型的实例在内存经过对齐后占用的字节大小 (内存对齐请参照百度百科,链接将会在下文给出)
public static var stride: Int { get }
/// 默认内存对齐单位
public static var alignment: Int { get }

内存对齐百度百科: baike.baidu.com/item/内存对齐/9…

这三个属性也是我们经常使用的,对应还有三个从实例对象中获取的方法

playgroud 介绍代码

struct Test {
let res1: Bool = false
let res2: Int = 0
let res3: Bool = false
}

print("\(MemoryLayout .stride)" ) // 24

因为这个类不是我们首要要介绍的,所以就只是大概介绍

内存指针

指针定义:

在计算机科学中,**指针(Pointer)**是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。

节选自百度百科: baike.baidu.com/item/指针/287…

Swift 中认为所有的 指针 相关操作都是不安全的,所以所有指针相关的结构体都会包含有前缀 Unsafe

Swift 中一共有8个结构体

Swift Objective-c 描述
UnsafePointer const T * 指针及其所指向的内存内容均不可变
UnsafeMutablePointer T * 指针及其所指向的内存内容均可变
UnsafeBufferPointer const T * [] 是一个指针数组内容均不可变
UnsafeMutableBufferPointer T * []
UnsafeRawPointer const void *
UnsafeMutableRawPointer void *
UnsafeBufferRawPointer void * []

首先要说一个实例类对象如何转化为指针

关于Swift中的指针的那些事
UnsafePointer

代码展示:

struct Obj {
    var name: Int = 5
}

var obj = Obj()

let pointer = withUnsafePointer(to: &obj, {$0})

print("\(pointer.pointee)")

这里注意了, UnsafePointer.pointee 只有 get 方法,也就是相当于我们获取了一个 let 的对象,遵从于 let 的相关要求。

注:这里多介绍一下 letvar 的区别

letvar 都是 swift 声明变量时候的前缀。在程序中,当我们声明变量的时候,在真实运行时候的环境里,程序便会为我们声明的变量初始化对应的内存。

let 声明的变量的内存内容,在声明初始化过后便不再可变

var 声明的变量的内存内容,在随后可以随时被修改。

这里我们使用的是 内存内容 ,也就是抽象的内存地址里的那些0、1的值。

对于 classletvar 的区别是这个变量能不能再被赋值一个新的类对象,而对原有对象的相关存储属性的修改,并不受影响。这是因为 class 的对象在声明时候的堆栈内存内容,只有8个字节(我们可以使用 MemoryLayout 获取),真实对象的存储在其他位置。所以我们能修改 let 指向的值

对于 structletvar 的区别就是 let 创建的对象,不论属性是用 let 还是 var ,我们都无法直接修改值。只能修改 var 修饰的。这个是因为 struct 对象是直接存储在声明时候的内存内容里。

对于UnsafeMutablePointer来说,pointee便是 setget 方法了,也就是说,我们可以直接修改对应内存地址的值了。

这里展示下如何获取UnsafeMutablePointer与修改相关值

struct Obj {
    var name: Int = 5

    init(name: Int = 5) {
        self.name = name
    }
}

var obj = Obj()

let pointer = withUnsafeMutablePointer(to: &obj, {$0})

print("Obj: \(obj)")

pointer.pointee = Obj(name: 10)

print("\(pointer.pointee)")

print("Obj: \(obj)")

当运行这段代码的时候,我们可以看到, obj 的内容也随着指针变化了。

注: obj 只能使用 var 声明,因为 Unsafe 类的调用需要使用 & ,同时有可能产生内存内容的变化,因此 let 定义下不符合相关要求。

接下来是我要讲的就是Swift中等同于 C语言void * 的指针 UnsafeRawPointer

关于Swift中的指针的那些事

有可能到这里就会有疑问了,我们已经有了UnsafePointer了,这个UnsafeRawPointer究竟有什么用呢?

在苹果的官方文档中,关于 UnsafeRawPointer 的定义为

A raw pointer for accessing untyped data.

一个用来访问未定义类型的指针

Overview

The UnsafeRawPointer type provides no automated memory management, no type safety, and no alignment guarantees. You are responsible for handling the life cycle of any memory you work with through unsafe pointers, to avoid leaks or undefined behavior.

UnsafeRawPointer 提供了没有自动内存管理,没有类型安全,与内存对齐控制保证。你有义务去自己管理你通过指针引用的任何内存的声明周期,以避免内存泄漏或者其余未定义(不安全)的行为

Memory that you manually manage can be either untyped or bound to a specific type. You use the UnsafeRawPointer type to access and manage raw bytes in memory, whether or not that memory has been bound to a specific type.

你手动管理的内存可以是未定义内存的,或者是被定义到一个指定的类型的。你可以使用UnsafeRawPointer去访问和管理未被定义的内存字节,而无需在意这段内存是否已经被绑定到一个指定的类型。

官方定义是这样的,大致思路就是,如果你有需求需要自己管理内存的时候,使用这个类。一般就是我们操作底层的一些代码,这个会需要被当成参数传入。

代码示例:

var i = 2

let pointerRaw = withUnsafeBytes(of: &i, {$0})

guard let pointer = pointerRaw.bindMemory(to: Int.self).baseAddress else { fatalError("这段代码理论上不能执行到这") }

let j = pointer.pointee

print("j:\(j)")

以上代码就是转换rawPointer和pointer的例子了。剩下的是bufferPointer,因为使用的地方较少了,故此不深入介绍了。

总结

指针相关中最重要的几个操作就是指针的生成,与将指针转化成实例对象。其中重要的就是 pointee 和几个 withUnsafeXX 的全局函数。

swift 中,指针的世界并不复杂,所有指针的操作都能转化为8个 UnsafeXX 类。我们的使用也是基于此的,我们如果使用某些底层方法需要指针,这个时候只需要将相应的指针对象生成即可传入。

结尾

一转眼已经元旦了,期间也想过写一些文章,但是对于没什么东西要写感到困惑。同时,这段时间也是一直在学习 Swift 的相关语言。因为个人项目中将要用到socket操作,于是决定用 Swift 重写 GCDAsyncSocket 。在重写过程中遇到了很多指针类似的问题,于是决定写一篇关于指针的教程。

Swift的socket库 SwiftAsyncSocket 目前正在完善中,TCP/IP协议基础功能已基本完善。近期将会正式开源

作者:chouheiwa

链接:https://juejin.im/post/5c2cc325518825480635dd39


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

常用算法深入学习实录

常用算法深入学习实录

张子言 / 电子工业出版社 / 2013-10 / 89.00元

对于任何一门编程语言来说,算法都是程序的“灵魂”。正是因为算法如此重要,所以笔者精心编写了本书,希望通过书中的内容引领广大读者一起探讨学习算法的奥秘,带领广大读者真正步入程序开发的高级世界。 本书共分15章,循序渐进、由浅入深地详细讲解算法的核心内容,并通过具体实例的实现过程演练各个知识点的具体用法。本书首先详细讲解算法的基础知识,剖析了将算法称为“程序灵魂”的原因。然后详细讲解算法技术的核......一起来看看 《常用算法深入学习实录》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器