十八、内存访问冲突、指针
2020-02-12 本文已影响0人
爱玩游戏的iOS菜鸟
内存访问冲突(Conflicting Access to Memory)
产生内存访问冲突的条件
内存访问冲突会在两个访问满足下列条件时发生:(3个条件同时满足)
- 至少一个是写入操作
- 它们访问的是同一块内存
- 它们的访问时间重叠(比如同一个函数内)
//不存在内存访问冲突
func plus(_ num: inout Int) -> Int{
num += 1
return num
}
var num = 1
print(plus(&num))
//存在内存访问冲突
var step = 1
func increment(_ num :inout Int){
num += step//同时访问和修改 访问冲突
//Simultaneous accesses to 0x100006668, but modification requires exclusive access.
}
increment(&step)
//存在内存访问冲突
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: 5)
Oscar.shareHealth(with: &maria)//OK
Oscar.shareHealth(with: &Oscar)//Error
//结构体访问冲突
var tulpe = (health: 10, energy: 20)
balance(&tulpe.health, &tulpe.energy)//Error
var holly = Player(name: "holly", health: 10, energy: 20)
balance(&holly.health, &holly.energy)//Error
不会内存访问冲突的情况
如果下面的条件可以满足,就说明重叠访问结构体的属性是安全的
- 只访问实例存储属性,不是计算属性或者类属性
- 结构体是局部变量而非全局变量
- 结构体要么没有被闭包捕获要么只被非逃逸闭包捕获
func test(){
var tulpe = (health: 10, energy: 20)
balance(&tulpe.health, &tulpe.energy)//Error
var holly = Player(name: "holly", health: 10, energy: 20)
balance(&holly.health, &holly.energy)
}
指针
Swift也有专门的指针类型,这些都被定性为"Unsafe",常见的有一下4种类型:
- UnsafePointer<Pointee>类似于const Pointee *
- 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
var age2 = 20
func test3(_ ptr:UnsafeMutableRawPointer){
ptr.storeBytes(of: 20, as: Int.self)
}
func test4(_ ptr:UnsafeRawPointer){
print(ptr.load(as: Int.self))
}
test3(&age2)
test4(&age2)//20
print(age)//20
指针的应用示例
var arr = NSArray(objects: 11,22,33,44)
arr.enumerateObjects { (obj, idx, stop) in
print(idx,obj)
if idx == 2 {//下标为2就停止遍历
stop.pointee = true
}
}
for (idx,obj) in arr.enumerated(){
print(idx , obj)
if idx == 2 {
break
}
}
- 获得指向某个变量的指针
var age = 11
var prt1 = withUnsafeMutablePointer(to: &age) { $0 }
var prt2 = withUnsafePointer(to: &age) { $0 }
prt1.pointee = 22
print(prt2.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))
print(age)
- 获得指向堆空间实例的指针
class Person { }
var person = Person()
var ptr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
var heapPtr = UnsafeRawPointer(bitPattern: ptr.load(as: UInt.self))
print(heapPtr!)
- 创建指针
var ptr = UnsafeRawPointer(bitPattern: 0x100001234)
//创建
var ptr1 = malloc(16)//分配16个字节的堆空间
//存
ptr1?.storeBytes(of: 11, as: Int.self)//11只占8个字节 所以只初始化前8个字节
ptr1?.storeBytes(of: 22, toByteOffset: 8, as: Int.self)//初始化 后8个字节
//取
print((ptr1?.load(as: Int.self))!)//11 取前8个字节
print((ptr1?.load(fromByteOffset: 8, as: Int.self))!)//22 取后8个字节
//销毁
free(ptr1)
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.storeBytes(of: 11, as: Int.self)// 初始化前8个字节
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 取后8个字节
ptr.deallocate()//销毁
//Int类型已确定
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)//3 * 8 24个字节
ptr.initialize(to: 11)
ptr.successor().initialize(to: 22)//后进指向的内存地址赋值 下一个Int空间
ptr.successor().successor().initialize(to: 33)
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()
class Person{
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
deinit {
print(name,"deinit")
}
}
var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 3)
ptr.initialize(to: Person(age: 10, name: "Jack"))
(ptr + 1).initialize(to: Person(age: 10, name: "Rose"))
(ptr + 2).initialize(to: Person(age: 10, name: "Kate"))
ptr.deinitialize(count: 3)
ptr.deallocate()
//Jack deinit
//Rose deinit
//Kate deinit
- 指针之间的转换
- unsafeBitCast是忽略类型的强制类型转换,不会因为数据类型的变化而改变原来的内存数据 —— 类似于C++中的reinterpret_cast
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.assumingMemoryBound(to: Int.self).pointee = 11
(ptr + 8).assumingMemoryBound(to: Double.self).pointee = 22.0//非泛型指针+8 就是偏移8个字节 和泛型指针有区别 注意!
print(unsafeBitCast(ptr, to: UnsafePointer<Int>.self).pointee)//11
print(unsafeBitCast(ptr + 8, to: UnsafePointer<Double>.self).pointee)//22.0
ptr.deallocate()
----
class Person { }
var person = Person()
var ptr = unsafeBitCast(person, to: UnsafeRawPointer.self)
print(ptr)