一、内存访问冲突
-
1.1、内存访问冲突会在两个访问满足下列条件时发生
(1)、至少一个是写入操作
(2)、它们访问的是同一块内存
(3)、它们访问的时间重叠(比如在同一个函数内)
-
如下:不存在 内存访问冲突
func plus(_ num: inout Int) -> Int { num + 1 } var number = 1 number = plus(&number) print(number)
-
如下:存在 内存访问冲突
内存访问冲突的原因:
num
和step
是同一块内存地址,同一时间访问就会出现内存访问冲突-
解决内存访问冲突的办法如下,这样
step
和num
就不是同一块内存地址var copyStep = step increment(©Step) step = copyStep print(step)
-
1.2、如下全局范围内的内存冲突
func balance(_ x: inout Int, _ y: inout Int) { let sum = x + y x = sum / 2 y = sum - x } var num1 = 42 var num2 = 30 balance(&num1, &num2) // OK balance(&num1, &num1) // Error struct Player { var name: String var health: Int var energy: Int mutating func shareHealth(with teammate: inout Player) { balance(&teammate.health, &health) } } var oscar = Player(name: "Oscar", health: 10, energy: 10) var maria = Player(name: "Maria", health: 5, energy: 10) oscar.shareHealth(with: &maria) // OK oscar.shareHealth(with: &oscar)// Error var tulpe = (health: 10, energy: 20) // Error balance(&tulpe.health, &tulpe.energy) var holly = Player(name: "Holly", health: 10, energy: 10) // Error balance(&holly.health, &holly.energy)
- 内存访问冲突的原因是:同一时间访问同一块内存地址
- 元组的各个成员是同一块内存地址,不可同时读取访问
-
1.3、如果下面的条件可以满足,就说明重叠访问结构体的属性是安全的
你只访问实例存储属性,不是计算属性或者类属性
结构体是局部变量而非全局变量
-
结构体要么没有被闭包捕获要么只被非逃逸闭包捕获
func balance(_ x: inout Int, _ y: inout Int) { let sum = x + y x = sum / 2 y = sum - x } struct Player { var name: String var health: Int var energy: Int mutating func shareHealth(with teammate: inout Player) { balance(&teammate.health, &health) } } func test() { var tulpe = (health: 10, energy: 20) balance(&tulpe.health, &tulpe.energy) var holly = Player(name: "Holly", health: 10, energy: 10) balance(&holly.health, &holly.energy) } test()
提示:上面的代码不会造成内存冲突,因为是放在
test()
里面,是在栈空间,在局部作用域内有效,那么就认为是安全的
二、指针
-
2.1、Swift中也有专门的指针类型,这些都被定性为“Unsafe”(不安全的),常见的有以下4种类型 , 含有
Mutable
代表可以修改内存UnsafePointer<Pointee> 类似于
const Pointee *
// const 不可修改UnsafeMutablePointer<Pointee> 类似于
Pointee *
UnsafeRawPointer 类似于
const void *
-
UnsafeMutableRawPointer 类似于
void *
var age = 10 func test1(_ ptr: UnsafeMutablePointer<Int>) { ptr.pointee += 10 } func test2(_ ptr: UnsafePointer<Int>) { print(ptr.pointee) } test1(&age) test2(&age) // 20 print(age) // 20
ptr.pointee
是取出ptr
所指向的数据var age = 10 func test3(_ ptr: UnsafeMutableRawPointer) { ptr.storeBytes(of: 20, as: Int.self) } func test4(_ ptr: UnsafeRawPointer) { print(ptr.load(as: Int.self)) } test4(&age) // 10 test3(&age) // 20 print(age) // 20
-
2.2、指针的应用示例:数组遍历
var arr = NSArray(objects: 11, 22, 33, 44)v arr.enumerateObjects { (obj, idx, stop) in print(idx, obj) if idx == 2 { // 下标为2就停止遍历 stop.pointee = true } } var arr = NSArray(objects: 11, 22, 33, 44) for (idx, obj) in arr.enumerated() { print(idx, obj) if idx == 2 { break } }
推荐 for 循环遍历,可读性高
-
2.3、获得指向某个变量的指针
var age = 11 var ptr1 = withUnsafeMutablePointer(to: &age) { $0 } var ptr2 = withUnsafePointer(to: &age) { $0 } ptr1.pointee = 22 print(ptr2.pointee) // 22 print(age) // 22 var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) } var ptr4 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) } ptr3.storeBytes(of: 33, as: Int.self) print(ptr4.load(as: Int.self)) // 33 print(age) // 33
提示:
$0
:withUnsafeMutablePointer(to: &T, <#T##body: (UnsafeMutablePointer<T>) throws -> Result)
返回值的类型就是闭包表达式的返回值类型 -
2.4、获得指向堆空间实例的指针
class Person {} var person = Person() var ptr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) } // person 变量的地址值 var personObjAddress = ptr.load(as: UInt.self) // 通过 person 变量的地址值 来获取 Person 在堆空间的地址值 var heapPtr = UnsafeRawPointer(bitPattern: personObjAddress ) print(heapPtr!) // Mems 工具 print(Mems.ptr(ofRef: person))
提示:
- personObjAddress 是 person变量的地址
- heapPtr 是 Person 在堆空间的地址值
-
2.5、创建指针(在 HandyJSON 里面会经常看到这些指针)
-
通过传变量地址 来获取 对象在堆空间的内存地址
UnsafeRawPointer(bitPattern: 变量地址)
var ptr = UnsafeRawPointer(bitPattern: 0x100001234)
-
malloc 来创建 指针
// 创建 var ptr = malloc(16) // 存 // 前 8 个字节放 11 ptr?.storeBytes(of: 11, as: Int.self) // 偏移 8 个字节(也就是后 8 个字节)放 22 ptr?.storeBytes(of: 22, toByteOffset: 8, as: Int.self) // 取 // 取前 8 个字节的值 print((ptr?.load(as: Int.self))!) // 11 // 偏移 8 个字节,取后 8 个字节的值 print((ptr?.load(fromByteOffset: 8, as: Int.self))!) // 22 // 销毁 指针 free(ptr)
提示:ByteOffset 代表位数偏移
-
UnsafeMutableRawPointer.allocate 来创建 指针
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1) // 前 8 个字节存值 11 ptr.storeBytes(of: 11, as: Int.self) // 后 8 个字节存值 22 ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self) // 取前 8 个字节 print(ptr.load(as: Int.self)) // 11 // 取后 8 个字节 print(ptr.advanced(by: 8).load(as: Int.self)) // 22 ptr.deallocate()
- byteCount: 字节数 alignment 对齐方式
-
UnsafeMutablePointer 泛型的来创建指针
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3) ptr.initialize(to: 11) // 0x0 第一个 Int ptr.successor().initialize(to: 22) // 0x8 第二个 Int ptr.successor().successor().initialize(to: 33) // 0x10 第三个 Int print(ptr.pointee) // 11 print((ptr + 1).pointee) // 22 print((ptr + 2).pointee) // 33 print(ptr[0]) // 11 print(ptr[1]) // 22 print(ptr[2]) // 33 //反初始化释放内存空间 ptr.deinitialize(count: 3) ptr.deallocate()
- capacity :容量,如果泛型是 Int,capacity: 3 ,代表 3 个 Int 类型的空间
- ptr.successor()后继:相当于跳 8 个字节
-
-
2.6、比较复杂的方式创建指针
class Person { var age: Int var name: String init(age: Int, name: String) { self.age = age self.name = name } deinit { print(name, "deinit") } } // 创建 3 个 Person 对象的指针 var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 3) ptr.initialize(to: Person(age: 10, name: "Jack")) (ptr + 1).initialize(to: Person(age: 11, name: "Rose")) (ptr + 2).initialize(to: Person(age: 12, name: "Kate")) ptr.deinitialize(count: 3) ptr.deallocate() // Jack deinit // Rose deinit // Kate
提示:一定要执行 :
ptr.deinitialize(count: 容量)
,否则会有内存泄漏 -
2.7、指针之间的转换
-
assumingMemoryBound 来转换
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1) ptr.assumingMemoryBound(to: Int.self).pointee = 11 (ptr + 8).assumingMemoryBound(to: Double.self).pointee = 22.0 ptr.deallocate()
-
unsafeBitCast 是忽略数据类型的强制转换,不会因为数据类型的变化而改变原来的内存数据,仅仅是最底层二进制数据的迁移,类似于C++中的
reinterpret_cast
class Person {} var person = Person() var ptr = unsafeBitCast(person, to: UnsafeRawPointer.self) print(ptr)
func unsafeBitCast<T, U>(_ x: T, to type: U.Type) -> U
第一个参数
T
:想把睡进行转换-
第二个参数
U.Type
:想转换成什么类型// 11 print(unsafeBitCast(ptr, to: UnsafePointer<Int>.self).pointee) // 22.0 print(unsafeBitCast(ptr + 8, to: UnsafePointer<Double>.self).pointee)
-