这篇文章水准不高,可能因为我自己能力有限,英文水平也就这样,自己能看懂,可能存在误人子弟的可能性,所以如果有人有机会看到了这边文章就当是一个小白的入门级的笔记吧!如果需要更深入的了解请查看文末的参考链接


为了将我的PhotoCutter适配Swfit3一看到一大堆的Unsafe[Mutable]Pointer的错误就是脑壳疼!最头疼的事没有找到这方面的中文资料,只有自己来弄了,然后记录一下,现在项目还是用OC,一天不写就生疏,怕以后来自己又忘记了,然后自己纪录一下吧…

这里我先粗略地介绍:
1.我的理解:UnsafeMutablePointer其实就是UnsafePointer的可以变化的类型,但是UnsafePointer又不允许你去改变指针元素

2.Unsafe[Mutable]RawPointer:在swift3以前为Unsafe[Mutable]Pointer<Void>,也就是c中的void *

3.Unsafe[Mutable]BufferPointer表示一连串的数组指针

withUnsafePointer/withUnsafeMutablePointer

比如下面我用c的方式创建和销毁了一个int型的指针a:

int \*a = malloc(sizeof(int));
\*a = 10;
free(a)
假设在swift中`var a:Int = 10`,现在我们的目的是想创建一个指针a,我们需要将`a`转成`*a`,我们需要怎么做呢?这里可以用到`withUnsafePointer/withUnsafeMutablePointer`
这两个函数会以swift类型和一个block为参数,而这个目的指针就是这个block的参数。也就是说你想将某一个swift类型的参数转换为一个指针result,这个result就是你想获得的指针,也就是下面两个例子中的ptr,希望我这个描述没有把你绕晕!
这里我也以swift.org上面的socket例子来写吧!
var addrin = sockaddr_in()

创建UnsafeMutablePointer

withUnsafeMutablePointer(to: &addrin) { ptr in
    //ptr:UnsafeMutablePointer\
}

创建UnsafePointer

withUnsafePointer(to: &addrin) { ptr in
    //ptr: UnsafePointer\
}

withUnsafeBytes/withUnsafeMutableBytes

通过withUnsafeBytes/withUnsafeMutableBytes获得的bytes只能在在函数closure里面进行使用,这个函数只相对于Data类型来获取bytes使用!

func unsafebytes() {
    guard let data = ".".data(using: .ascii) else{ return }
    data.withUnsafeBytes { (byte:UnsafePointer) -> Void in
        print(byte) 
    }
}
unsafebytes()

withMemoryRebound

我们可以使用withMemoryRebound函数,来将一个类型的指针转换为另外一个类型的指针,使用这个函数的时候也有一些需要注意点,在[UnsafeRawPointer Migration] (1)的介绍中说到:Conversion from UnsafePointer<T> to UnsafePointer<U> has been disallowed,所以只能将UnsafePointer<Int>转换为UnsafeMutablePointer<UInt8>.

UnsafePointer -> UnsafeMutablePointer

var a = 10
withUnsafePointer(to: &a) { a_pt in
    a_pt.withMemoryRebound(to: UInt8.self, capacity: 1, { a_pt_uint8 in
           //a_pt_uint8:UnsafeMutablePointer           
    })
}

具体的使用场景:
在使用socket的时候需要bind或者connect的时候
这个函数的具体使用场景在[UnsafeRawPointer Migration] (1)中也有提到。

sockaddr_in -> sockaddr

var addrin = sockaddr_in()
let sock = socket(PF_INET, SOCK_STREAM, 0)
let result = withUnsafePointer(to: &addrin) {
    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
        connect(sock, $0, socklen_t(MemoryLayout.stride))
    }
}

assumingMemoryBound

UnsafeRawPointer转换为UnsafePointer<T>类型,也就是swift3之前的UnsafePointer<Void>UnsafePointer<T>

这个和前面提到的函数withMemoryRebound的区别就是:

assumingMemoryBound可以看成是withMemoryRebound的一个特例,即:

assumingMemoryBoundUnsafePointer<Void>UnsafePointer<T>

withMemoryReboundUnsafePointer<U>UnsafeMutablePointer<T>

代码示例:

let strPtr = UnsafeMutablePointer\.allocate(capacity: 1)
let rawPtr = UnsafeRawPointer(strPtr)
let intPtr = rawPtr.assumingMemoryBound(to: Int.self)

bindMemory

绑定一个类型\到已经被分配的内存空间,返回一个绑定在self内存上UnsafePointer<T>的指针,需要注意的是这个函数是用于Unsafe[Mutable]RawPointer。

/// - Precondition: The memory is uninitialized.
/// - Postcondition: The memory is bound to 'T' starting at `self` continuing
///   through `self` + `count` * `MemoryLayout.stride`
/// - Warning: Binding memory to a type is potentially undefined if the
///   memory is ever accessed as an unrelated type.
操作 内存状态 类型
rawptr = allocate() uninitialized None
tptr = rawptr.bindMemory(T) uninitialized bound to T
tptr.initialize() initialized bound to T

从上面的表格结合文档里面对于bindMemory的说明来看,我对于bindMemory的理解就是,使用函数之前这块内存空间是没有被初始化的,使用bindMemory的目的是将T绑定到self后面self + count MemoryLayout<T>.stride长度的的这块内存空间上来。但是绑定上来并不代表初始化了,此时这个内存空间仍然是没有初始化的,所以最后需要调用函数initialize的函数来初始化!

用这个函数同样可以把`void
`的C类型转换为Swift的类型。关于Custom memory allocation
这个函数的使用可能会有问题…先上一段我自己理解的代码吧

let a = 100
let a_rawptr = UnsafeMutableRawPointer.allocate(bytes: MemoryLayout\.size, alignedTo: MemoryLayout\.alignment)
let bind_rawptr = a_rawptr.bindMemory(to: Int.self, capacity: MemoryLayout\.stride)
bind_rawptr.initialize(to: a)

unsafeBitCast

返回一个翻译成某一特定类型的值!,这个会破坏Swift的类型系统

特别注意️:
不到万不得已不要使用这个函数

实战

PhotoCutter为了适配Swift3,这其中大部分和指针相关的东西需要适配,我开始看到这些也是懵逼的,根本不懂怎么改,只有自己去慢慢学。我的方法可能很差,就目前而言是适配了,下面贴上我的修改的代码吧!

Swift2.x

options = CFDictionaryCreate(kCFAllocatorDefault,
            UnsafeMutablePointer(UnsafePointer(keys)),
            UnsafeMutablePointer(UnsafePointer(values)),
            2,
            &kcKeysBack,
            &kcValuesBack)

Swift3.0

fileprivate func buffer(to type:T.Type, source:[T]) -> UnsafeMutablePointer{
    var buffer = UnsafeMutablePointer.allocate(capacity: source.count)
    for idx in 0...size, alignedTo: MemoryLayout.alignment)
        let bindptr = m_ptr.bindMemory(to: type, capacity: 1)
        bindptr.initialize(to: source[idx])
         let pty = UnsafeRawPointer(m_ptr)
        buffer.advanced(by: idx).pointee = pty
    }
    return buffer
}

调用:

let keys:[CFString] = [
            kCGImageSourceCreateThumbnailWithTransform,
            kCGImageSourceCreateThumbnailFromImageIfAbsent,
            kCGImageSourceThumbnailMaxPixelSize]
var keybuffer = buffer(to: CFString.self, source: keys)
/\*
这之间做你的相关操作
*/
keybuffer.deallocate(capacity: keys.count)

到这里我对于swift3指针相关的东西,就告一段落了,文章写的很粗糙,望见谅,明天开始公司要求做持续化集成了,如果你看到这个而且想和我交流的话可以在博客上的微博和我取得联系,因为我是真的不懂博客。。。以后有时间会去学习一下前端相关的东西,练手项目应该就是我这个博客!

最后还是希望去看看C 语言指针 5 分钟教程


参考文献:

Use Swift With Cocoa

UnsafeRawPointer Migration Swift.org

Swift 3.0 Unsafe World

StackOverflow Question

Binding memory type

Swift 中的指针使用(这个是之前版本的介绍,就参考一下用unsafeBitCast,同时文章中提到的C 语言指针 5 分钟教程)

指针)