Swift三部曲(一):指针的使用

背景

大部分状况下作Swift开发是不须要使用指针的,也不建议使用,可是有时候写比较底层的东西就须要了。最近一段时间刚好我在写的一些库的须要用到指针,可是Swift关于指针的使用并无很详细的文档,致使写起代码来十分费劲,因此总结了一下。Runtime的文章不少,可是Swift的不多,因此我准备了Swift三部曲,介绍底层相关知识,有些以前看过了解过,但谈不上很深刻,因此会边写边研究,分别是:html

Swift三部曲(一):指针的使用
Swift三部曲(二):内存布局
Swift三部曲(三):方法派发git

第一篇就是本文,第二篇和第三篇还没写,不过最近会陆续写完。github

MemoryLayout

// 单位均为字节
MemoryLayout<T>.size       // 类型T须要的内存大小
MemoryLayout<T>.stride     // 类型T实际分配的内存大小(因为内存对齐原则,会多出空白的空间)
MemoryLayout<T>.alignment  // 内存对齐的基数
复制代码

指针分类

pointers.png

  • unsafe:不安全的,并非真的不安全,大概是提示开发者少用。
  • Write Access:可写入。
  • Collection:像一个容器,可添加数据。
  • Strideable:指针可以使用 advanced 函数移动。
  • Typed:是否须要指定类型(范型)。

C 和 Swift关于指针的对照表:swift

C Swift 注解
const Type * UnsafePointer<Type> 指针可变,指针指向的内存值不可变
Type * UnsafeMutablePointer<Type> 指针和指针指向的内存值都可变
ClassType * const * UnsafePointer<UnsafePointer<Type>> 指针的指针:指针不可变,指针指向的类可变
ClassType ** UnsafeMutablePointer<UnsafeMutablePointer<Type>> 指针的指针:指针和指针指向的类都可变
ClassType ** AutoreleasingUnsafeMutablePointer<Type> 做为OC方法中的指针参数
const void * UnsafeRawPointer 指针指向的内存区,类型未定
void * UnsafeMutableRawPointer 指针指向的内存区,类型未定
StructType * OpaquePointer c语言中的一些自定义类型,Swift中并未有相对应的类型
int a[] UnsafeBufferPointer/UnsafeMutableBufferPointer 一种数组指针

typed pointer(类型指针)

Swift中的指针分为两大类, typed pointer 指定数据类型指针, raw pointer 未指定数据类型的指针(原生指针)。数组

typed pointer
UnsafePointer
UnsafeMutablePointer
UnsafeBufferPointer
UnsafeMutableBufferPointer

UnsafeMutablePointer

被UnsafeMutablePointe引用的内存有三种状态:安全

  1. Not Allocated:内存没有被分配,这意味着这是一个 null 指针,或者是以前已经释放过
  2. Allocated but not initialized:内存进行了分配,可是值尚未被初始化
  3. Allocated and initialized:内存进行了分配,而且值已经被初始化

allocatebash

// 绑定类型并分配内存
// allocate是类方法
// capacity: Int表示向系统申请 capacity 个数的对应泛型类型的内存
let pointer = UnsafeMutablePointer<Int>.allocate(capacity: MemoryLayout<Int>.stride)
// let pointer = UnsafeMutablePointer<CCInfo>.allocate(capacity: MemoryLayout.stride(ofValue: CCInfo()))
复制代码

initialize闭包

// 初始化, 对于Int, Float, Double这些基本数据类型,分配内存以后会有默认值0
pointer.initialize(to: 12)
// pointer.pointee 为 12

// 赋值
pointer.pointee = 10

复制代码

deinitializeapp

// 与 initialize: 配对使用的 deinitialize: 用来销毁指针指向的对象
// 回到初始化值以前,没有释放指针指向的内存,指针依旧指向以前的值
pointer.deinitialize(count: 1)

复制代码

deallocateasync

// 与 allocate(capacity:) 对应的 deallocate() 用来释放以前申请的内存
pointer.deallocate()
复制代码

注意其实在这里对于 Int 这样的在 C 中映射为 int 的 “平凡值” 来讲,deinitialize 并非必要的,由于这些值被分配在常量段上。可是对于像类的对象或者结构体实例来讲,若是不保证初始化和摧毁配对的话,是会出现内存泄露的。因此没有特殊考虑的话,不论内存中究竟是什么,保证 initialize: 和 deinitialize 配对会是一个好习惯。

UnsafePointer

UnsafePointer 是不可变的,C 中 const 修饰的指针对应 UnsafePointer (最多见的应该就是 C 字符串的 const char * 了)。

  • UnsafePointer中的pointee属性只能get不能set。
  • UnsafePointer中没有allocate方法。

初始化
能够由UnsafeMutablePointer、OpaquePointer或其余UnsafePointer建立一个UnsafePointer指针。其余与UnsafeMutablePointer相似。

//经过另外一个变量指针初始化一个`UnsafePointer`常量指针
let pointer = UnsafeMutablePointer<Int>.allocate(capacity: MemoryLayout<Int>.stride)
pointer.pointee = 20
let pointer1 = UnsafePointer<Int>.init(pointer)
print(pointer1.pointee)  // 20
复制代码

将指针引用的内存做为不一样的类型访问

withMemoryRebound

将内存临时从新绑定到其余类型。

var int8: Int8 = 123
let int8Pointer = UnsafeMutablePointer(&int8)
int8Pointer.withMemoryRebound(to: Int.self, capacity: 8) { ptr in
    print(ptr.pointee) // 123
}
复制代码

bindMemory

该方法绑定内存为指定类型并返回一个UnsafeMutablePointer<指定类型>的指针,用到了指向内存的原始指针。

let intPointer = UnsafeRawPointer(int8Pointer).bindMemory(to: Int.self, capacity: 1)
print(intPointer.pointee) // 123

复制代码

在使用 bindMemory方法将原生指针绑定内存类型,转为类型指针的时候,一次只能绑定一个类型,例如:将一个原生指针绑定Int类型,不能再绑定Bool类型。

assumingMemoryBound

该方法意思是直接转换这个原始指针为一个UnsafeMutablePointer<指定类型>的指针。

let strPtr = UnsafeMutablePointer<CFString>.allocate(capacity: 1)
let rawPtr = UnsafeRawPointer(strPtr)
let intPtr = rawPtr.assumingMemoryBound(to: Int.self)
复制代码

UnsafeBufferPointer

UnsafeBufferPointer表示一组连续数据指针。BufferPointer实现了Collection,所以能够直接使用Collection中的各类方法来遍历操做数据,filter,map...,Buffer能够实现对一块连续存在空间进行操做,相似C中的数组的指针。 可是一样的,这个UnsafeBufferPointer是常量,它只能获取到数据,不能经过这个指针去修改数据。与之对应的是UnsafeMutableBufferPointer指针。

var array = [1, 2, 3, 4]
// 遍历
let ptr = UnsafeBufferPointer.init(start: &array, count: array.count)
ptr.forEach { element in
    print(element) // 1,2,3,4
}

//遍历
array.withUnsafeBufferPointer {  ptr in
    ptr.forEach {
        print($0) // 1,2,3,4
    }
}

复制代码

UnsafeBufferPointer 可使用 baseAddress 属性,这个属性包含了缓冲区的基本地址。

let array: [Int8] = [65, 66, 67, 0]
puts(array)  // ABC
array.withUnsafeBufferPointer { (ptr: UnsafeBufferPointer<Int8>) in
    puts(ptr.baseAddress! + 1) //BC
}

复制代码
let count = 2
let pointer = UnsafeMutablePointer<Int>.allocate(capacity: count)
pointer.initialize(repeating: 0, count: count)

defer {
    pointer.deinitialize(count: count)
    pointer.deallocate()
}

pointer.pointee = 42 // pointer 指向的内存地址存放数值 42
pointer.advanced(by: 1).pointee = 6 // pointer 下一个内存地址存放数值 6,即 pointer 指向的起始地址加 Int 类型的步幅再移动 1 位,就其起始地址
   pointer.pointee
pointer.pointee
pointer.advanced(by: 1).pointee

let bufferPointer = UnsafeBufferPointer(start: pointer, count: count)
for (index, value) in bufferPointer.enumerated() {
    print("value \(index): \(value)") // value 0: 42, value 1: 6
}

复制代码

UnsafeMutableBufferPointer

可变的序列指针,UnsafeMutableBufferPointer拥有对指向序列修改的能力:

let pointer = UnsafeMutablePointer<Int>.allocate(capacity: count)
let bufferPointer = UnsafeMutableBufferPointer<Int>.init(start: pointer, count: 5)  // 拓展为5各元素的大小
bufferPointer[0] = 120
bufferPointer[1] = 130   //进行修改,其余未修改的内容将产生随机值
bufferPointer.forEach { (a) in
    print(a) // 120, 130, 120054000649232, 73, 105553129173888
}
print(bufferPointer.count) // 5

复制代码

情况跟UnsafeBufferPointer有点相似,只是在初始化的时候,须要借助UnsafeMutablePointer。 并不能直接使用已经存在序列进行初始化。 值的注意的是:若是一个序列被初始化以后,没有给每个元素赋值的话,这些元素的值都将出现随机值

raw pointer(原生指针)

raw pointer
UnsafeRawPointer
UnsafeMutableRawPointer
UnsafeRawBufferPointer
UnsafeMutableRawBufferPointer

UnsafeMutableRawPointer

UnsafeMutableRawPointer 用于访问和操做非类型化数据的原始指针。

// 分配内存, byteCount: 表示总共须要的字节数, 表示 Int 类型的对齐方式
let pointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: MemoryLayout<Int>.alignment)

// 将给定值存储在指定偏移量的原始内存中
pointer.storeBytes(of: 0x00060001, as: UInt32.self)

// 从pointer引用的内存 用UInt8实例加载(即第一个字节用UInt8实例加载)
let value = pointer.load(as: UInt8.self)
print(value) // 1

// pointer.storeBytes(of: 42, as: Int.self)
// let value = pointer.load(as: Int.self)
// print(value) 42

let offsetPointer = pointer.advanced(by: 2)
// let offsetPoint = pointer + 2 // 偏移 2个字节, 若是偏移3个字节,下面的操做就会越界了
let offsetValue = offsetPointer.load(as: UInt16.self) // 将第三个和第四个字节做为UInt16实例加载

print(offsetValue) // 6
pointer.deallocate()

复制代码

注:方法 storeBytes 和 load 分别是用来存储和读取字节数的。

UnsafeRawPointer

用于访问非类型化数据的原始指针。UnsafeRawPointer只能由其余指针用init方法获得,与UnsafePointer相似,没有allocate静态方法。可是,与UnsafeMutableRawPointer相似的有两种绑定方法bindMemory和assumingMemoryBound,绑定成UnsafePointer指针。

// 访问不一样类型的相同内存
var uint64: UInt64 = 257
let rawPointer = UnsafeRawPointer(UnsafeMutablePointer(&uint64))
let int64PointerT =  rawPointer.load(as: Int64.self)
let uint8Point = rawPointer.load(as: UInt8.self)

print(int64PointerT) // 257
print(uint8Point) // 1

// 257 = 1 0000 0001 而UInt8 表示存储8个位的无符号整数,即一个字节大小, 2^8 = 256, [0, 255], 超出8个位范围的没法加载,因此打印为1

复制代码

UnsafeRawBufferPointer 与 UnsafeMutableRawBufferPointer

引用Swift内存赋值探索二: 指针在Swift中的使用的描述:

UnsafeRawBufferPointer和UnsafeMutableRawBufferPointer 指代的是一系列的没有被绑定类型的内存区域。咱们能够理解成他们实际上就是一些数组,再绑定内存以前,其中包含的元素则是每个字节。 在底层,基本数据单元的复制是有效的,另外没有被 retain 和 stong 的也是可以安全的复制的,一样的,对于来自C API的对象也可以安全的复制。对于原声的Swift类型,有的包含了引用的对象的复制则有可能失败,可是咱们可使用指针对他们的值进行复制,这样的结果是有效的。若是咱们强行对一下发类型进行复制,不必定有效,除非使用像C语言中的APImemmove().来操做

UnsafeRawBufferPointer和UnsafeMutableRawBufferPointer是内存视图,尽管咱们知道它指向的内存区域,可是它并不拥有这块内存的引用。复制UnsafeRawBufferPointer 类型的变量不会复制它的内存;可是初始化一个集合到另外一个新的集合过程会复制集合中的引用内存。

总结:

  1. 内存中的每一个字节都被视为一个独立于内存绑定类型的 UInt8 值, 与该内存中保存的值的类型无关。
  2. UnsafeRawBufferPointer / UnsafeMutableRawBufferPointer 实例是内存区域中原始字节的视图。
  3. 经过原始缓冲区从内存中读取是一种无类型操做, UnsafeMutableRawBufferPointer 实例能够写入内存, UnsafeRawBufferPointer 实例不能够。
  4. 若是要类型化,必须将内存绑定到一个类型上。
let pointer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout<Int>.alignment)
pointer.copyBytes(from: [1, 2, 3])
pointer.forEach {
    print($0) // 1, 2, 3
}

复制代码

Memory Access

要经过类型化操做访问底层内存,必须将内存绑定到一个简单的类型。

typed pointer
withUnsafePointer
withUnsafeMutablePointers
withUnsafeBytes
withUnsafeMutableBytes

withUnsafePointer/withUnsafeMutablePointer

Swift 中不能像 C 里那样使用 & 符号直接获取地址来进行操做。若是咱们想对某个变量进行指针操做,咱们能够借助 withUnsafePointer 或 withUnsafeMutablePointer 这两个辅助方法。withUnsafePointer 或 withUnsafeMutablePointer 的差异是前者转化后的指针不可变,后者转化后的指针可变。

基本数据类型

var a = 0

withUnsafePointer(to: &a) { ptr in
    print(ptr) // 0x00007ffeeccb3b40
}

a = withUnsafePointer(to: &a) { ptr in
    return ptr.pointee + 2
    // 此时, 会新开辟空间, 令a指向新地址, 值为2,
}

// 修改指针指向的内存值
var b = 42
withUnsafeMutablePointer(to: &b) { ptr in
    ptr.pointee += 100   // 未开辟新的内存空间, 直接修改a所指向的内存值
}
print(b)   // 142

var arr = [1, 2, 3]
withUnsafeMutablePointer(to: &arr) { ptr in
    ptr.pointee[0] = 10
}
print(arr)   // [10, 2, 3]

arr.withUnsafeBufferPointer { ptr in
    ptr.forEach{
        print("\($0)")  // 10 2 3
    }
}

// 修改内存值
arr.withUnsafeMutableBufferPointer { ptr in
    ptr[0] = 100

    ptr.forEach {
        print("\($0)") // 100 2 3
    }
}


复制代码

获取 struct 类型实例的指针

struct User {
    var name: Int = 5

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

var user = User()

let pointer = withUnsafeMutablePointer(to: &user, {$0})
print(user) // user
pointer.pointee = User(name: 10)
print("\(pointer.pointee)") // User(name: 10)
print(user) // User(name: 10)

复制代码

获取 class 类型实例的指针
获取 class 类型实例的指针和上面不一样,不是使用withUnsafePointer 或 withUnsafeMutablePointer,而是使用下面讲到的Unmanaged,之因此放在这里,是想由于这里讲到获取对象指针,因此附带讲一下。

func headPointerOfClass() -> UnsafeMutablePointer<Int8> {
    let opaquePointer = Unmanaged.passUnretained(self as AnyObject).toOpaque()
    let mutableTypedPointer = opaquePointer.bindMemory(to: Int8.self, capacity: MemoryLayout<Self>.stride)
    return UnsafeMutablePointer<Int8>(mutableTypedPointer)
}

复制代码

withUnsafeBytes/withUnsafeMutableBytes

可使用withUnsafeBytes/withUnsafeMutableBytes获取实例的字节数。

// 打印字符串
let string = "hello"
let data = string.data(using: .ascii)
data?.withUnsafeBytes{ (ptr: (UnsafePointer<Int8>)) in
    print(ptr.pointee) // 104 = 'h'
    print(ptr.advanced(by: 1).pointee)  // 101 = 'e'
}


// 打印结构体
struct SampleStruct {
    let number: UInt32
    let flag: Bool
}

MemoryLayout<SampleStruct>.size       // returns 5
MemoryLayout<SampleStruct>.alignment  // returns 4
MemoryLayout<SampleStruct>.stride     // returns 8

var sampleStruct = SampleStruct(number: 25, flag: true)

withUnsafeBytes(of: &sampleStruct) { bytes in
    for byte in bytes {
        print(byte) // 25 0 0 0 1
    }
}

let bytes = withUnsafeBytes(of: &sampleStruct) { bytes in
    return bytes // 这里会有奇怪的bug!
}

print("Horse is out of the barn!", bytes) // undefined !!! 

复制代码

注:

  1. 不要从 withUnsafeBytes 中返回指针。
  2. 绝对不要让指针逃出 withUnsafeBytes(of:) 的做用域范围。这样的代码会成为定时炸弹,你永远不知道它何时能够用,而何时会崩溃。

Unmanaged<T>(非托管对象)

若是直接使用指针,那么就须要咱们手动管理内存,这个并很差办,因此苹果引入了Unmanaged来管理引用计数,Unmanaged 可以将由 C API 传递过来的指针进行托管,咱们能够经过Unmanaged标定它是否接受引用计数的分配,以便实现相似自动释放的效果;同时,若是不是使用引用计数,也可使用Unmanaged 提供的release函数来手动释放,这比在指针中进行这些操做要简单不少。关于Unmanaged swifter.tips这篇TOLL-FREE BRIDGING 和 UNMANAGED文章好像不少地方都讲错了。

一个 Unmanaged 实例封装有一个 CoreFoundation 类型 T,它在相应范围内持有对该 T 对象的引用。

将一个对象声明为非托管方法有两种:

  • passRetained:增长它的引用计数。
  • passUnretained:不增长它的引用计数。

从一个 Unmanaged 实例中获取一个 Swift 值的方法有两种:

  • takeRetainedValue():返回该实例中 Swift 管理的引用,并在调用的同时减小一次引用次数。
  • takeUnretainedValue():返回该实例中 Swift 管理的引用而 不减小 引用次数。

这看起来仍是不知道什么时候使用passRetained和passUnretained,什么时候使用takeRetainedValue和takeUnretainedValue,苹果提出了Ownership Policy

  • 若是一个函数名中包含Create或Copy,则调用者得到这个对象的同时也得到对象全部权,返回值Unmanaged须要调用takeRetainedValue()方法得到对象。调用者再也不使用对象时候,Swift代码中不须要调用CFRelease函数放弃对象全部权,这是由于Swift仅支持ARC内存管理,这一点和OC略有不一样。
  • 若是一个函数名中包含Get,则调用者得到这个对象的同时不会得到对象全部权,返回值Unmanaged须要调用takeUnretainedValue()方法得到对象。
let bestFriendID = ABRecordID(...)

// Create Rule - retained
let addressBook: ABAddressBook = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()

if let
    // Get Rule - unretained
    bestFriendRecord: ABRecord = ABAddressBookGetPersonWithRecordID(addressBook, bestFriendID)?.takeUnretainedValue(),
    // Create Rule (Copy) - retained
    name = ABRecordCopyCompositeName(bestFriendRecord)?.takeRetainedValue() as? String
{
    println("\(name): BFF!")
    // Rhonda Shorsheimer: BFF!
}

复制代码

这些函数能够经过函数名知道该怎么使用Unmanaged,但不少时候在使用的不是这种命名的C函数,

Alamofire的NetworkReachabilityManager.swift中就有一段调用C方法使用了Unmanaged。

@discardableResult
    open func startListening() -> Bool {
        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = Unmanaged.passUnretained(self).toOpaque()

        let callbackEnabled = SCNetworkReachabilitySetCallback(
            reachability,
            { (_, flags, info) in
                let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(info!).takeUnretainedValue()
                reachability.notifyListener(flags)
            },
            &context
        )

        let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)

        listenerQueue.async {
            self.previousFlags = SCNetworkReachabilityFlags()
            self.notifyListener(self.flags ?? SCNetworkReachabilityFlags())
        }

        return callbackEnabled && queueEnabled
    }

复制代码

由于self对象的使用是在当前做用域内,也就是startListening方法内部,咱们能保证使用的时候对象一直存活,因此使用的passUnretained和takeUnretainedValue。

class Person {
    func eat() {
        print(#file, #line, "eat now")
    }
}

func callbackFunc(userPtr: UnsafeMutableRawPointer?) {
    if userPtr == nil { return }
    let user = Unmanaged<Person>.fromOpaque(userPtr!).takeRetainedValue()
    user.eat()
}
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        var user = Person()
        let userPtr = Unmanaged<Person>.passRetained(user).toOpaque()
        callback(callbackFunc, userPtr)
    }
}

复制代码

咱们看到因为使用了闭包,user对象的使用超出了当前做用域也就是viewDidLoad方法,因此须要使用takeRetainedValue和passRetained。

let userPtr = Unmanaged<Person>.passRetained(user).toOpaque()

复制代码

使用passRetained()会建立一个被retained的指向这个对象的指针,这样能够保证在C中被调用的时候这个对象还在那,不会被销毁,这个方法会产生一个Unmanaged的实例变量,而后经过toOpaque() 方法转换为 UnsafeMutableRawPointer。

let user = Unmanaged<Person>.fromOpaque(userPtr!).takeRetainedValue()

复制代码

利用Unmanaged相反的方法,取出user对象,这种方法更加安全,能够保证对象在传递过程当中一直存在,而且直接得到对象。

非托管对象使用周期超过了编译器认为的生命周期,好比超出做用域,必须手动 retain 这个对象,也就是使用 passRetained 方法。一旦你手动 retain 了一个对象,就不要忘记 release 掉它,方法就是调用非托管对象的 release 方法,或者用 takeRetainedValue 取出封装的对象,并将其管理权交回 ARC。但注意,必定不要对一个用 passUnretained 构造的非托管对象调用 release 或者 takeRetainedValue,这会致使原来的对象被 release 掉,从而引起异常。

测试

unmanaged仍是有点难的,我在其余地方看到这段代码,你们能够在Playground试一试,若是有知道全部答案的,能够留言讨论一下。

class SomeClass {
    let text: Int

    init(text: Int) {
        self.text = text
    }

    deinit {
        print("Deinit \(text)")
    }
}

do {
    let unmanaged = Unmanaged.passRetained(SomeClass(text: 0))
    unmanaged.release()
}

do {
    let _ = Unmanaged.passUnretained(SomeClass(text: 1))
}

do {
    let unmanaged = Unmanaged.passRetained(SomeClass(text: 2))

    let _ = unmanaged.retain()
    unmanaged.release()

    Unmanaged<SomeClass>.fromOpaque(unmanaged.toOpaque()).release()

    unmanaged.release()
}

do {
    let opaque = Unmanaged.passRetained(SomeClass(text: 3)).toOpaque()
    Unmanaged<SomeClass>.fromOpaque(opaque).release()
}

do {
    let unmanaged = Unmanaged.passRetained(SomeClass(text: 4))
    let _ = unmanaged.takeUnretainedValue()
    unmanaged.release()
}

do {
    let unmanaged = Unmanaged.passRetained(SomeClass(text: 5))
    let _ = unmanaged.takeRetainedValue()
}

复制代码

函数指针

在C中有回调函数,当swift要调用C中这类函数时,可使用函数指针。Swift中能够用@convention 修饰一个闭包

类型 注解
@convention(swift) 代表这个是一个swift的闭包
@convention(block) 代表这个是一个兼容oc的block的闭包,能够传入OC的方法
@convention(c) 代表这个是兼容c的函数指针的闭包,能够传入C的方法

第二个类型是这三个当中可能最经常使用的,当你在Swift使用Aspects中会用到,我用Swift写的Aspect使用的时候也能够这样:

let wrappedBlock: @convention(block) (AspectInfo, Int, String) -> Void = { aspectInfo, id, name in

}
let block: AnyObject = unsafeBitCast(wrappedBlock, to: AnyObject.self)
test.hook(selector: #selector(Test.test(id:name:)), strategy: .before, block: )
复制代码

unsafeBitCast

咱们能够看看源码Builtin.swift

/// - Parameters:
///   - x: The instance to cast to `type`.
///   - type: The type to cast `x` to. `type` and the type of `x` must have the
///     same size of memory representation and compatible memory layout.
/// - Returns: A new instance of type `U`, cast from `x`.
@inlinable // unsafe-performance
@_transparent
public func unsafeBitCast<T, U>(_ x: T, to type: U.Type) -> U {
  _precondition(MemoryLayout<T>.size == MemoryLayout<U>.size,
    "Can't unsafeBitCast between types of different sizes")
  return Builtin.reinterpretCast(x)
}
复制代码

unsafeBitCast 是很是危险的操做,它会将一个指针指向的内存强制按位转换为目标的类型,而且只进行了简单的 size 判断。由于这种转换是在 Swift 的类型管理以外进行的,所以编译器没法确保获得的类型是否确实正确,你必须明确地知道你在作什么。

let block: AnyObject = unsafeBitCast(wrappedBlock, to: AnyObject.self)
复制代码

这段代码就是将oc的block的闭包转成Anyobject类型。

参考文章:
Unsafe Swift: Using Pointers And Interacting With C
UnsafeRawPointer Migration
Swift内存赋值探索二: 指针在Swift中的使用
Unmanaged
在Swift 3.0调用C语言API
Swift 中的指针使用
深度探究HandyJSON(一) Swift 指针的使用

相关文章
相关标签/搜索