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

背景

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

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

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

MemoryLayout

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

指针分类

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

C 和 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:内存进行了分配,并且值已经被初始化

allocate

// 绑定类型并分配内存
// 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

deinitialize

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

deallocate

// 与 allocate(capacity:) 对应的 deallocate() 用来释放之前申请的内存
pointer.deallocate()

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

UnsafePointer

UnsafePointer<T> 是不可变的,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<T>表示一组连续数据指针。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<T> 实例封装有一个 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 指针的使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容