在Objective-C中我們經常會用到指針,有些方法也需要直接去操作指針,今天我們就來看看如何在Swift中使用指針。
下面這個Objective-C方法會返回一個int指針,或者說C術語里面的(int *):
[cpp] view plaincopy
@interface PointerBridge : NSObject {
int count;
}
- (int *) getCountPtr;
@end
@implementation PointerBridge
- (instancetype) init {
self = [super init];
if(self) {
count = 23;
}
return self;
}
- (int *) getCountPtr {
return &count;
}
@end
上面的代碼定義了一個PointerBridge類,它包含getCountPtr方法,這個方法返回一個值為23的int型內存地址。 這個Int其實是count的實例,它在構造方法init中被賦值為23。
我把這段代碼放在一個Objective-C的頭文件中,然后把這個頭文件import到我的橋接頭文件(XXX-bridging-header.h)中,這樣就可以在Swift中使用。然后我在Swift中創建一個名為bridge的PointerBridge實例,然后獲得getCountPtr() 方法的返回值…
[cpp] view plaincopy
let bridge = PointerBridge()
let theInt = bridge.getCountPtr()
print(theInt)
print(theInt.memory)
在Xcode中按住Option鍵點擊theInt檢查它的類型,你會發現他的Swift類型是UnsafeMutablePointer<Int32>。這是指向Int型的指針,和Int型不一樣,它僅僅是指向它的指針。
如果運行這個程序然后執行這段Swift代碼,我們會發現theInt在命令行中輸出類似0x00007f8bdb508ef8這樣的內存地址,然后,然后我們會看到memory成員變量輸出的值23 。訪問指針指向的內存通常返回其底層指向的對象,在這個例子中就是原來的32位int(在Swift中就是Int32)。
現在讓Objective-C類支持設置count的值。
[cpp] view plaincopy
@interface PointerBridge : NSObject {
int count;
}
- (int *) getCountPtr;
- (void) setCount:(int)newCount;
@end
@implementation PointerBridge
- (instancetype) init {
self = [super init];
if(self) {
count = 23;
}
return self;
}
- (int *) getCountPtr {
return &count;
}
- (void) setCount:(int)newCount {
count = newCount;
}
@end
我們可以調用setCount()方法來修改count的值。因為theInt是一個指針,所以通過setCount修改count也會更新theInt.memory。別忘了內存地址是不會變的,變的是值。
也就是說,下面的代碼會在命令行中打印數字23, 然后打印數字1000。
[cpp] view plaincopy
let bridge = PointerBridge()
let theInt = bridge.getCountPtr()
print(theInt.memory) // 23
bridge.setCount(1000)
print(theInt.memory) // 1000
如果想避免每次都寫.memory,有一條捷徑就是把.memory賦值給一個變量:
[cpp] view plaincopy
let bridge = PointerBridge()
let theInt = bridge.getCountPtr()
let countVal = theInt.memory
print(countVal) // 23
就像之前一樣, 命令行會輸出23。然而,如果我們像之前那樣調用setCount()方法修改count的值,問題出現了:
[cpp] view plaincopy
let bridge = PointerBridge()
let theInt = bridge.getCountPtr()
let countVal = theInt.memory
print(countVal) // 23
bridge.setCount(1000)
print(countVal) // 23
出現問題的原因是countVal是通過值(value)來賦值的。賦值的時候值(value)就是23,所以countVal有它自己的內存地址,這個地址永久地保存了23這個值,所以已經失去了指針的特性。countVal現在只是一個普通的 Int32 型。
如果我們想要做和上面相反的事情呢?不是用Int型來給count賦值,而是傳入一個指針呢?
我們假設在Objective-C的代碼中有如下的一個方法:
[cpp] view plaincopy
- (void) setCountPtr:(int *)newCountPtr {
count = *newCountPtr;
}
這個方法很作,其實就是把newCountPtr重新賦值給count,但在Swift開發中你確實會碰到這樣一些需要傳入指針的場景。用這么作的方式只是為了向你展示如何在 Swift 中創建指針類型,然后傳入到Objective-C的方法中。
你可能會簡單的認為只要使用一個類似&的引用操作符就可以傳入Int值,就像你在C中所做的那樣。在Objective-C中你可以這樣寫:
[cpp] view plaincopy
int mcount = 500; [self setCountPtr:&mcount];
這段代碼可以成功地把count的值更新為500。然而在Swift中,通過自動補全你會發現它更復雜(而且更冗長)。它需要傳入一個UnsafeMutablePointer<Int32> 類型的newCountPtr變量。
我知道這個類型很惡心,而且它看起來確實很復雜。但是,事實上它相當簡單,特別是在你了解Objective-C中的指針的情況下。如果要創建一個UnsafeMutablePointer<Int32> 類型的對象,我們只需要調用構造方法,這個構造方法需要傳入的參數就是指針的大小(你應該知道C的指針是不存儲類型的,所以它也不會存儲大小的信息)。
[cpp] view plaincopy
let bridge = PointerBridge() let theInt = bridge.getCountPtr() print(theInt.memory)
// 23 let newIntPtr = UnsafeMutablePointer<Int32>.alloc(1) newIntPtr.memory
= 100 bridge.setCountPtr(newIntPtr) print(theInt.memory) // 100
需要給UnsafeMutablePointer<Int32>構造方法傳入的參數就是需要分配空間的對象的個數,所以我們傳入1即可,因為我們只需要一個Int32對象 。然后,只需要把我們之前對memory所做的事情反過來,我們就可以為我們新建的指針賦值。終,我們只需要簡單滴把newIntPtr傳入到setCountrPtr方法中,再把之前theInt指針的值打印出來,我們就會發現它的值已經被更新為100。
UnsafeMutablePointer<T>類型的兄弟類型UnsafePointer<T>從根本上說只是C指針的一個抽象。你可以把它們看作Swift的可選類型,這樣更容易理解。它們不是直接等于一個確切的值,而是在一個確切的值上面做了一層抽象。它們的類型是泛型,這樣就可以允許其使用其他的值,而不單單是Int32。比如你需要傳入一個Float對象那么你可能需要UnsafeMutablePointer<Float>。
重點是:你不是把一個Int強轉為UnsafeMutablePointer<Int>,因為指針不是簡單地一個Int值。所以,如果需要創建一個新的對象,你需要調用構造方法UnsafeMutablePointer<Int>(count: Int)。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。