Swift編程語言爲了能與Objective-C與C語言兼容,而引入了指針類型。儘管官方不建議頻繁使用指針類型,但不少時候,使用指針能完成更多、更靈活的任務。好比,咱們要實現一個交換兩個整數值的函數的時候就不得不動用指針了。就表達上,Swift使用UnsafePointer來表示指向一個常量的指針;使用UnsafeMutablePointer來表示指向一個變量的指針,也是比較直觀。不過目前大多Swift開發者對於Swift指針類型的運用不太嫺熟,並且官方的編程指南也沒怎麼提,因此俺這裏將寫一篇博文來詳細給各位介紹一下Swift中的指針類型以及其各類用法~ios
// // 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函數用於方便地得到一個變量的指針。swift
myTest函數描述了UnsafeMutablePointer的advancedBy方法以及memory方法的使用以及功能。數組
myTest2函數描述了assignBackwardFrom函數的使用以及功能緩存
myTest3函數描述了assignFrom函數的使用以及功能閉包
myTest4函數描述了distanceTo函數的使用以及功能編程語言
這裏你們要注意的是,在Swift中因爲函數是一個閉包,因此只有「函數對象」這個概念,因此不存在指向函數的指針,只有函數對象引用。若是有外部C語言的API存在指向函數指針類型,則須要用@convention(c)去作轉換處理。ide
咱們下面舉一個例子,主要描述二維數組若是經過指針進行操做的例子函數
// // 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標準~