Swift —— 指针

1.指针

为什么说指针是不安全的呢?主要以下几点:

  • 比如我们在创建一个对象的时候,是需要在堆区分配内存空间的,但是这个内存空间的生命周期是有限的,也就意味着如果我们使用指针指向这块内存空间,如果当前内存空间的生命周期到了(也就是引用计数为0了),那么当前的指针就成了未定义的行为,也就是野指针。
  • 我们创建的内存空间是有边界的,比如创建一个大小为10的数组,这个时候通过指针访问到index = 11 的位置,这个时候就越界了,访问了一个未知的内存空间。
  • 指针类型和内存空间的值类型不一致,也是不安全的。

2. 指针类型

swift中的指针分为2类,typed pointer (指定数据类型指针)raw pointer(未指定数据类型指针,也叫原生指针),基本上我们接触的指针类型有以下几种

  • unsafePointer,相当于oc中的const T *,指针以及指向内容都不可变
  • unsafeMutablePointer,相当于oc中的 T *,指针以及指向内容都可变
  • unsafeRawPointer,相当于oc中的const Void *,指针指向的内存区域未定
  • unsafeMutableRawPointer,相当于oc中的const Void *,指针指向的内存区域未定
  • unsafeBufferPointer,连续的内存空间
  • unsafeMutableBufferPointer
  • unsafeRawBufferPointer
  • unsafeMutableRawBufferPointer

3. 原始指针的使用

接下来使用raw pointer 储存4个整形的数据,这里使用unsafeMutablePointer.

let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)

for i in  0..<4 {
    p.storeBytes(of: i, as: Int.self)
}


for i in  0..<4 {
    let value = p.load(fromByteOffset: i * 8, as: Int.self)
    print("index\(i) value:\(value)")
}

但是这里运行后发现取出来的值和想要的不一样,这是因为存的时候指针没有位移相应的位置,也就是步长。
在这里插入图片描述
在IOS里面有测量当前大小的工具,分别是

  • MemoryLayout.size
  • MemoryLayout.stride
  • MemoryLayout.alignment

例如下面代码

struct LGTeacher {
    var age: Int = 18
}

print(MemoryLayout<LGTeacher>.size)
print(MemoryLayout<LGTeacher>.stride)
print(MemoryLayout<LGTeacher>.alignment)

运行后发现都是8

在这里插入图片描述
而在结构体添加一个bool属性

struct LGTeacher {
    var age: Int = 18
    var sex: Bool = true
}

然后重新运行,发现这里打印的值就不一样了。这里可以知道,size是struct结构体的大小,stride是占用内存的实际大小,alignment是对其的大小。
在这里插入图片描述
所以这里知道,我们储存的时候先要移动 i * 步长信息的位置,然后在储存值。

for i in  0..<4 {
    p.advanced(by: i * MemoryLayout<Int>.stride).storeBytes(of: i , as: Int.self)
}

这样运行后就可以看到打印的是期望的值了
在这里插入图片描述
当然,在使用完指针之后,需要调用deallocate来释放内存空间。

p.deallocate()

4. 泛型指针的使用

泛型指针,也叫类型指针,指定当前指针已经绑定到了具体的类型。

获取withUnsafePointer的方式有两种,一种是通过已有变量获取:

如果闭包是最后一个参数的话,可以写成尾随闭包。

var age = 18
withUnsafePointer(to: &age){ ptr in
    print(ptr)
}

指针的具体内容使用pointee访问,pointee代表指针指向的具体数据类型,所以下列代码是可行的。

age = withUnsafePointer(to: &age){ ptr in
    ptr.pointee + 21
}

但是需要注意的是,这里的pointee是只读属性,所以下面的代码是不可行的。

age = withUnsafePointer(to: &age){ ptr in
    ptr.pointee +=  21
}

但是如果使用的是withUnsafeMutablePointer的话,那么pointee就可变了。

withUnsafeMutablePointer(to: &age) { ptr in
    ptr.pointee +=  21   
}

这个时候可以看到age的值也变了。
在这里插入图片描述
一种是直接内存分配:

var age = 10
let tPtr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
tPtr.initialize(to: age)

这里也有两种方式来初始指针内存,一种是使用:

struct LGTeacher {
    var age: Int
    var height: Double
}
var tPtr = UnsafeMutablePointer<LGTeacher>.allocate(capacity: 5)

tPtr[0] = LGTeacher(age: 18, height: 180.0)
tPtr[1] = LGTeacher(age: 18, height: 180.0)
tPtr.deinitialize(count: 5)
tPtr.deallocate()

一种是使用:

struct LGTeacher {
    var age: Int
    var height: Double
}
var tPtr = UnsafeMutablePointer<LGTeacher>.allocate(capacity: 5)

tPtr.initialize(to: LGTeacher(age: 18, height: 180))
tPtr.advanced(by: MemoryLayout<LGTeacher>.stride).initialize(to:
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值