Swift中的指针类型
Swift编程语言为了能与Objective-C与C语言兼容,而引入了指针类型。尽管官方不建议频繁使用指针类型,但很多时候,使用指针能完成更多、更灵活的任务。比如,我们要实现一个交换两个整数值的函数的时候就不得不动用指针了。就表达上,Swift使用UnsafePointer来表示指向一个常量的指针;使用UnsafeMutablePointer来表示指向一个变量的指针,也是比较直观。不过目前大多Swift开发者对于Swift指针类型的运用不太娴熟,而且官方的编程指南也没怎么提,所以俺这里将写一篇博文来详细给各位介绍一下Swift中的指针类型以及其各种用法~
// // ViewController.swift // SwiftTest // // Created by Zenny Chen on 16/4/1. // Copyright © 2016年 GreenGames Studio. All rights reserved. // import Cocoa /// 对输入一个数组缓存的每个元素进行求和,然后返回求和结果 <br/> /// 最后,将该数组缓存中的中间一个元素修改为求和的结果值 func myTest(array: UnsafeMutablePointer<Int>, count: Int) -> Int { var sum = 0 var i = 0 while i < count { // tmp的值为array第i个元素的值 let tmp = array.advancedBy(i).memory sum += tmp i += 1 } array.advancedBy(count / 2).memory = sum return sum } /// 将srcArray中后一半的元素拷贝到dstArray中的前一半中去 func myTest2(dstArray: UnsafeMutablePointer<Int>, srcArray: UnsafePointer<Int>, count: Int) { // 这里通过构造方法将UnsafePointer<Int>转为UnsafeMutablePointer<Int>类型 dstArray.assignBackwardFrom(UnsafeMutablePointer<Int>(srcArray).advancedBy(count / 2), count: count / 2 + 1) } /// 将srcArray中前一半的元素拷贝到dstArray中的前一半中去 func myTest3(dstArray: UnsafeMutablePointer<Int>, srcArray: UnsafePointer<Int>, count: Int) { // 这里从srcArray的第二个元素开始,拷贝count / 2 + 1个元素 dstArray.assignFrom(UnsafeMutablePointer<Int>(srcArray.advancedBy(1)), count: count / 2 + 1) } /// 用于测试distanceTo方法 func myTest4(array: UnsafeMutablePointer<Int>, count: Int) { // 这里,distanceTo就相当于distanceTo的参数所在的元素位置与array所在的位置的差 // 相当于(distanceTo参数的地址 - array的地址) / sizeof(Int) let distance = array.distanceTo(array.advancedBy(count / 2)) print("distance = \(distance)") } class ViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // 声明一个变量a,并用10对它初始化 var a = 10 let ptr: UnsafeMutablePointer<Int> = UnsafeMutablePointer<Int>(bitPattern: 0x1000) let bptr = UnsafeMutableBufferPointer(start:&a, count:1) bptr[0] += 20 let cptr = UnsafeMutablePointer<Int>(bptr.baseAddress) cptr.memory += 30 print("a = \(a)") var x = 10 let p = getMutablePointerType(&x) p.memory += 100 print("x = \(x)") var array = [1, 2, 3, 4, 5] let value = myTest(&array, count: array.count) print("value = \(value), and array is: \(array)") let doubleArray: [[Int]] = [ [1, 2, 3], [4, 5], [9, 10, 11, 12, 13] ] for arr in doubleArray { print("array length: \(arr.count)") } array = [Int](count: 5, repeatedValue: 0) myTest2(&array, srcArray: [1, 2, 3, 4, 5], count: array.count) print("array is: \(array)") array = [Int](count: 5, repeatedValue: 0) myTest3(&array, srcArray: [1, 2, 3, 4, 5], count: array.count) print("array is: \(array)") myTest4(&array, count: array.count) } override var representedObject: AnyObject? { didSet { // Update the view, if already loaded. } } }
在上述代码例子中,我们主要描述了UnsafeMutablePointer类型的特性与常用方法的使用描述。一开始,我们定义了一个
getMutablePointerType函数用于方便地获得一个变量的指针。
myTest函数描述了UnsafeMutablePointer的advancedBy方法以及memory方法的使用以及功能。
myTest2函数描述了assignBackwardFrom函数的使用以及功能
myTest3函数描述了assignFrom函数的使用以及功能
myTest4函数描述了distanceTo函数的使用以及功能
这里大家要注意的是,在Swift中由于函数是一个闭包,所以只有“函数对象”这个概念,所以不存在指向函数的指针,只有函数对象引用。如果有外部C语言的API存在指向函数指针类型,则需要用@convention(c)去做转换处理。
我们下面举一个例子,主要描述二维数组如果通过指针进行操作的例子
// // ViewController.swift // SwiftTest // // Created by Zenny Chen on 16/4/1. // Copyright © 2016年 GreenGames Studio. All rights reserved. // import Cocoa /// 此函数用于将一个变量转换为指向该变量的常量指针对象 func getConstPointerType<T> (ptr: UnsafePointer<T>) -> UnsafePointer<T> { return ptr } /// 此函数用于将一个变量转换为指向该变量的变量指针对象 func getMutablePointerType<T> (ptr: UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T> { return ptr } class ViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. var array = [[1, 2, 3], [4, 5, 6, 7]] // 定义一个指向元素为数组类型的数组对象的指针 let p = getMutablePointerType(&array) // 此时,p.memory的类型为[Int] // 将p的第1个元素的第2个元素的值加10 p.memory[1] += 10 // 将p的第2个元素的第4个元素的值加100 p.advancedBy(1).memory[3] += 100 print("array = \(array)") let funcRef: @convention(c) () -> Int32 = MyCFunc print("value is: \(funcRef())") } override var representedObject: AnyObject? { didSet { // Update the view, if already loaded. } } }
通过上述代码,我们可以注意到,在Swift中,一个二维数组其实就是以数组对象作为元素的数组对象。该数组对象的每个元素是一个数组对象引用。因此在用指针操作的时候,其实就是先对某一个数组对象进行操作,访问其里面的一个元素。
此外,Swift中的UnsafeMutablePointer、UnsafeMutableBufferPointer等指针类型对象对具体对象的引用都属于弱引用,也就是说它们的引用以实际对象来看是完全被别名化的,指针对实际对象的操作跟直接用实际对象的引用对对象操作的效果是完全等价的,所以Apple官方称这些类型为“non-owning pointer”。下面举一个例子:
class MyClass { deinit { print("MyClass is destroyed!") } } class ViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() var myObj: MyClass? = MyClass() var objptr = UnsafeMutableBufferPointer(start: &myObj, count: 1) // 这句话与myObj = nil的效果完全一样, // 这句话执行完之后就会打印:MyClass is destroyed! objptr.baseAddress.memory = nil } }
以下展示用Swift来玩二级指针,其实与C语言的也类似:
class ViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() var a = 0 var size = sizeofValue(a) print("size = \(size)") size = sizeof(ViewController) print("size = \(size)") var arr = [1, 2, 3] print("The size is: \(sizeofValue(arr))") var ptr = getMutablePointer(&a) ptr[0] += 10 print("The value is: \(a)") let pp = getMutablePointer(&ptr) var b = 0 ptr = getMutablePointer(&b) pp[0][0] += 2 print("b = \(b)") pp[0] = getMutablePointer(&a) pp[0][0] -= 2 print("a = \(a)") } }
看完上述Swift中指针操作的场景之后,本人蛋疼地用C++也封装了一些基本类型,可基本模拟出Swift对指针的操作访问模式。
// // hello.cpp // CDemo // // Created by Zenny Chen on 16/4/1. // Copyright © 2016年 GreenGames Studio. All rights reserved. // #include <iostream> #include <functional> using namespace std; #define object auto struct Int { private: int mValue; public: Int(void) : mValue(0) { } Int(int i) : mValue(i) { } inline Int& operator = (int value) { mValue = value; return *this; } inline Int& operator = (Int& obj) { mValue = obj.mValue; return *this; } inline operator int& (void) { return mValue; } inline operator const int (void) const { return mValue; } inline size_t size(void) const { return sizeof(mValue); } inline Int* address(void) { return this; } inline const char* typeName(void) const { return "Int"; } }; template <typename T> struct ptr { private: T *mPtr; public: ptr(void) : mPtr(NULL){ } ptr(T *t) : mPtr(t) { } inline T& memory(void) { return *mPtr; } inline T& memory(int index) { return mPtr[index]; } inline const T& memory(void) const { return *mPtr; } inline const T& memory(int index) const { return mPtr[index]; } inline ptr<T> advancedBy(int count) { return ptr<T>(&mPtr[count]); } inline ptr<T>& operator = (T* t) { mPtr = t; return *this; } inline ptr<T>& operator = (ptr<T>& p) { mPtr = p; return *this; } inline ptr<T>* address(void) { return this; } inline size_t size(void) const { return sizeof(mPtr); } inline uintptr_t value(void) const { return (uintptr_t)mPtr; } }; template <typename T, size_t S> struct myArray { private: T mArray[S]; public: myArray(void) { memset(mArray, 0, sizeof(mArray)); } myArray(T arr[S]) { memcpy(mArray, arr, sizeof(mArray)); } inline myArray<T, S>& operator = (myArray<T, S>& arr) { memcpy(mArray, arr.mArray, sizeof(mArray)); return *this; } inline myArray<T, S> *address(void) { return this; } inline T& operator [] (size_t index) { return mArray[index]; } inline size_t size(void) const { return sizeof(mArray); } inline size_t count(void) const { return S; } }; template <typename FP> class func : public function<FP> { private: void *mp; public: func(FP *fun) : function<FP>(fun) { mp = (void*)fun; } inline size_t size(void) const { return sizeof(void*); } inline ptr< func<FP> > address(void) { return this; } inline uintptr_t functionAddress(void) { return (uintptr_t)mp; } }; #pragma mark - 以下为对本语言系统的使用 static void MySwap(ptr<Int> a, ptr<Int> b) { int tmp = a.memory(); a.memory() = b.memory(); b.memory() = tmp; } extern "C" void CPPTest(void) { Int a = 100; printf("size of a = %zu\n", a.size()); // object可表示任一对象,并且其类型根据 = 右操作数进行推导 object b = a; printf("b type is: %s\n", b.typeName()); // 指向变量的指针 ptr<Int> p = a.address(); p.memory() += 10; printf("a = %d\n", a); printf("size of p = %zu\n", p.size()); // 指向常量的指针 ptr<const Int> cp = a.address(); // error ==> cp.memory() -= 10; printf("memory = %d\n", cp.memory()); // 指向指针的指针 ptr< ptr<Int> > pp = p.address(); printf("content is: %d\n", pp.memory().memory()); pp.memory() = NULL; // 指针p变为空 printf("p value is: %zu\n", p.value()); myArray<Int, 3> arr = (Int[]){ 1, 2, 3 }; printf("size is: %zu, count is: %zu\n", arr.size(), arr.count()); // 指向一个数组首地址的指针 p = arr[0].address(); printf("element 1 = %d\n", p.advancedBy(1).memory()); printf("element 2 = %d\n", p.memory(2)); // 将指针指向数组第二个元素的地址 p = arr[1].address(); printf("p[1] = %d\n", p.memory(1)); // 指向数组的指针 ptr< myArray<Int, 3> > pArr = arr.address(); printf("element 2 = %d\n", pArr.memory()[2]); // 数组赋值 myArray<Int, 3> arr2; arr2 = arr; arr2[0]++; arr2[1]--; pArr = arr2.address(); printf("arr2[0] = %d\n", pArr.memory()[0]); printf("arr2[1] = %d\n", pArr.memory()[1]); // 通过指针间接将arr的值赋给pArr所指向的数组对象arr2 pArr.memory() = arr; printf("arr2[0] = %d\n", arr2[0]); printf("arr2[1] = %d\n", arr2[1]); // 关于cv限定符对复杂类型的修饰 // 相当于:const int* const *qq = &cp; ptr<const ptr<const Int>> qq = cp.address(); // qq.memory() = NULL; Error! // qq.memory().memory() = 0; // Error! a = qq.memory().memory(); // OK printf("a = %d\n", a); qq = NULL; // OK printf("qq value is: %d\n", qq.value()); Int x = 10, y = -10; // 定义一个函数对象 func<void(ptr<Int>, ptr<Int>)> fun = MySwap; printf("fun size is: %zu\n", fun.size()); fun(x.address(), y.address()); printf("x = %d, y = %d\n", x, y); ptr< func<void(ptr<Int>, ptr<Int>)> > pFunc = fun.address(); pFunc.memory()(x.address(), y.address()); printf("x = %d, y = %d\n", x, y); printf("fun object address: %.16tX\n", fun.address().value()); printf("MySwap address: %.16tX\n", fun.functionAddress()); }
各位可以实践一下,用的是GNU++14标准~