Swift之UnsafeMutableRawPointer

用于访问和处理无类型数据的原始指针。

UnsafeMutableRawPointer 类型不提供自动内存管理,类型安全性和对齐保证。你有责任处理你创造的任何不安全内存的生命周期,以避免泄漏或不确定的行为。

您手动管理的内存可以 未类型化绑定 特定类型。无论该内存是否已绑定到特定类型,您可以使用 UnsafeMutableRawPointer 类型访问和管理内存中的原始字节。

一、了解指针的内存状态

UnsafeMutableRawPointer 实例引用的内存可以处于几种状态之一。 许多指针操作只能应用于内存处于特定状态的指针--你必须跟踪正在使用的内存的状态,并了解不同操作对状态的更改。 内存可以是未类型化和未初始化的,可以绑定到类型并进行未初始化,或者可以绑定到类型并初始化为一个值。 最后,先前分配的内存可能已经被释放,那么剩下的现有指针引用了未分配的内存。

二、原始未初始化的内存

刚刚分配的原始内存处于未初始化,未类型状态。 未初始化的内存必须先使用类型的值进行初始化,然后才能与任何类型的操作一起使用。

您可以使用诸如initializeMemory(as: from:)moveInitializeMemory(as: from: count:)之类的方法将原始内存绑定到一种类型,并使用一个值或一系列值对其进行初始化。 要将未初始化的内存绑定到类型而不进行初始化,请使用bindMemory(to: count:)方法。 这些方法都返回类型化的指针,以进一步对存储器进行类型化的访问。

三、类型化内存

绑定到某个类型的内存(无论是初始化的还是未初始化的),通常使用类型化的指针(例如 UnsafePointerUnsafeMutablePointer 的实例)进行访问。 可以使用 UnsafeMutablePointer 方法执行初始化,赋值和取消初始化。

绑定到某个类型的内存只有在取消初始化之后或者如果绑定的类型是 trivial type(普通类型),才能重新绑定到其他类型。 取消初始类型化的内存不会解除绑定该内存的类型。 已经取消初始化的内存可以重新使用相同类型的值初始化,也可以绑定到一个新的类型,或者释放内存。

注意:普通类型可以一点一点地复制,而无需间接或引用计数操作。 通常,不包含强引用或弱引用或其他间接形式的原生 Swift 类型都是普通类型,像导入的C结构体和枚举。

当以原始字节的形式读取或写入内存时,并且该内存绑定到一种类型,你必须确保满足所有对齐要求。仅当绑定类型为普通类型时才可以用原始字节的形式写入类型化内存。

四、原始指针算术

使用原始指针的指针算术是在字节级别执行的。 当您添加原始指针或从原始指针中减去时,结果是一个新的原始指针偏移了该字节数。 以下示例分配了四个字节的内存,并在所有四个字节中存储了 0xFF

let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 1)
bytesPointer.storeBytes(of: 0xFFFF_FFFF, as: UInt32.self)
// Load a value from the memory referenced by 'bytesPointer'
let x = bytesPointer.load(as: UInt8.self)       // 255  :1个字节8位最大值
// Load a value from the last two allocated bytes
let offsetPointer = bytesPointer + 2
let y = offsetPointer.load(as: UInt16.self)     // 65535 :1个字节16位最大值

上面的代码将值 0xFFFF_FFFF 存储到新分配的4字节中,然后将第一个字节加载为 UInt8 实例,将第三个和第四个字节加载为 UInt16实例。

永远记住要释放您自己分配的所有内存。

bytesPointer.deallocate()

五、隐式转换和桥接

当使用 UnsafeMutableRawPointer 参数调用函数或方法时,可以传递该特定指针类型的实例,传递兼容指针类型的实例,或者使用 Swift 的隐式桥接传递兼容的指针。

例如,以下代码示例中的 print(address: as:)函数将UnsafeMutableRawPointer 实例作为其第一个参数:

func print<T>(address p: UnsafeMutableRawPointer, as type: T.Type) {
    let value = p.load(as: type)
    print(value)
}

Swift中 的典型情况一样,您可以使用 UnsafeMutableRawPointer 实例调用 print(address:as :) 函数。 此示例将传递 rawPointer为初始参数。

// 'rawPointer' points to memory initialized with `Int` values.
let rawPointer: UnsafeMutableRawPointer = ...
print(address: rawPointer, as: Int.self)
// Prints "42"

由于类型化指针在作为参数传递时可以隐式转换为原始指针,因此您还可以使用任何可变的类型化指针实例调用 print(address: as:)

let intPointer: UnsafeMutablePointer<Int> = ...
print(address: intPointer, as: Int.self)
// Prints "42"

另外,您可以使用 Swift隐式桥接将指针传递给实例或数组的元素。 使用 inout 语法隐式创建指向任何类型的实例的指针。 以下示例在调用print(address: as:)时使用隐式桥接将指针传递给 value

 var value: Int = 23
 print(address: &value, as: Int.self)
 // Prints "23"

使用 inout 语法传递数组时,会隐式创建指向数组元素的可变指针。 此示例在调用 print(address: as:) 时使用隐式桥接将指针传递给 numbers 的元素。

var numbers = [5, 10, 15, 20]
print(address: &numbers, as: Int.self)
// Prints "5"

重要提示:通过隐式桥接实例或数组元素而创建的指针仅在被调用函数执行期间有效。 函数执行后转义要使用的指针是未定义的行为。 特别是,在调用UnsafeMutableRawPointer 初始化程序时,请勿使用隐式桥接。

 var number = 5
 let numberPointer = UnsafeMutableRawPointer(&number)
 // Accessing 'numberPointer' is undefined behavior.
let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)
for i in 0...3 {
    p.advanced(by: i * 8).storeBytes(of: i + 1, as: Int.self)
}
for i in 0...3 {
    let value = p.load(fromByteOffset: i * 8, as: Int.self)
    print("index\(i), value: \(value)")
}
p.deallocate()
---
index0, value: 1
index1, value: 2
index2, value: 3
index3, value: 4
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容