[譯]淺談 Objective-C 指針和 Swift2

2018-06-19 15:07 更新

該譯文獨(dú)家授權(quán)給SwiftGG

原文鏈接:Objective-C Pointers and Swift 2: A Simple Guide

原文日期:2015/08/23

譯者:mmoaay

校對(duì):numbbbbb

定稿:shanksyang

本文寫(xiě)于 2015 年 8 月 23 日,與 Xcode7 Beta 版和 Swift 2 兼容

在 Swift 中使用 Objective-C

在 Swift 中讀 C 指針

下面這個(gè) Objective-C 方法會(huì)返回一個(gè) int 指針,或者說(shuō) C 術(shù)語(yǔ)里面的 (int *)

@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

<!--more-->

上面的代碼定義了一個(gè) PointerBridge 類(lèi),它包含 getCountPtr 方法,這個(gè)方法返回一個(gè)值為 23 的 int內(nèi)存地址。 這個(gè) Int 其實(shí)是 count 的實(shí)例,它在構(gòu)造方法 init 中被賦值為 23 。

我把這段代碼放在一個(gè) Objective-C 的頭文件中,然后把這個(gè)頭文件 import 到我的橋接頭文件(XXX-bridging-header.h)中,這樣就可以在 Swift 中使用。然后我在 Swift 中創(chuàng)建一個(gè)名為 bridgePointerBridge 實(shí)例,然后獲得 getCountPtr() 方法的返回值…

let bridge = PointerBridge()
let theInt = bridge.getCountPtr()
print(theInt)
print(theInt.memory)

在 Xcode 中按住 Option 鍵點(diǎn)擊 theInt 檢查它的類(lèi)型,你會(huì)發(fā)現(xiàn)他的 Swift 類(lèi)型是 UnsafeMutablePointer<Int32>。這是指向 Int 型的指針,和 Int 型不一樣,它僅僅是指向它的指針

如果運(yùn)行這個(gè)程序然后執(zhí)行這段 Swift 代碼,我們會(huì)發(fā)現(xiàn) theInt 在命令行中輸出類(lèi)似 0x00007f8bdb508ef8 這樣的內(nèi)存地址,然后,然后我們會(huì)看到 memory 成員變量輸出的值 23 。訪(fǎng)問(wèn)指針指向的內(nèi)存通常返回其底層指向的對(duì)象,在這個(gè)例子中就是原來(lái)的 32 位 int(在 Swift 中就是 Int32

現(xiàn)在讓 Objective-C 類(lèi)支持設(shè)置 count 的值。

@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

我們可以調(diào)用 setCount() 方法來(lái)修改 count 的值。因?yàn)?theInt 是一個(gè)指針,所以通過(guò) setCount 修改 count 也會(huì)更新 theInt.memory。別忘了內(nèi)存地址是不會(huì)變的,變的是值。

也就是說(shuō),下面的代碼會(huì)在命令行中打印數(shù)字 23, 然后打印數(shù)字 1000。

let bridge = PointerBridge()
let theInt = bridge.getCountPtr()
print(theInt.memory) // 23
bridge.setCount(1000)
print(theInt.memory) // 1000

如果想避免每次都寫(xiě) .memory,有一條捷徑就是把.memory 賦值給一個(gè)變量:

let bridge = PointerBridge()
let theInt = bridge.getCountPtr()
let countVal = theInt.memory
print(countVal) // 23

就像之前一樣, 命令行會(huì)輸出 23。然而,如果我們像之前那樣調(diào)用 setCount() 方法修改 count 的值,問(wèn)題出現(xiàn)了:

let bridge = PointerBridge()
let theInt = bridge.getCountPtr()
let countVal = theInt.memory
print(countVal) // 23

 
bridge.setCount(1000)
print(countVal) // 23

出現(xiàn)問(wèn)題的原因是 countVal 是通過(guò)值(value)來(lái)賦值的。賦值的時(shí)候值(value)就是23,所以 countVal 有它自己的內(nèi)存地址,這個(gè)地址永久地保存了 23 這個(gè)值,所以已經(jīng)失去了指針的特性。 countVal 現(xiàn)在只是一個(gè)普通的 Int32 型。

在 Swift 中創(chuàng)建 C 指針

如果我們想要做和上面相反的事情呢?不是用 Int 型來(lái)給 count 賦值,而是傳入一個(gè)指針呢?

我們假設(shè)在 Objective-C 的代碼中有如下的一個(gè)方法:

- (void) setCountPtr:(int *)newCountPtr {
    count = *newCountPtr;
}

這個(gè)方法很作,其實(shí)就是把 newCountPtr 重新賦值給 count,但在 Swift 開(kāi)發(fā)中你確實(shí)會(huì)碰到這樣一些需要傳入指針的場(chǎng)景。用這么作的方式只是為了向你展示如何在 Swift 中創(chuàng)建指針類(lèi)型,然后傳入到 Objective-C 的方法中。

你可能會(huì)簡(jiǎn)單的認(rèn)為只要使用一個(gè)類(lèi)似 & 的引用操作符就可以傳入 Int 值,就像你在 C 中所做的那樣。在 Objective-C 中你可以這樣寫(xiě):

int mcount = 500;
[self setCountPtr:&mcount];

這段代碼可以成功地把 count 的值更新為 500。然而在 Swift 中,通過(guò)自動(dòng)補(bǔ)全你會(huì)發(fā)現(xiàn)它更復(fù)雜(而且更冗長(zhǎng))。它需要傳入一個(gè)UnsafeMutablePointer<Int32> 類(lèi)型的 newCountPtr 變量。

我知道這個(gè)類(lèi)型很惡心,而且它看起來(lái)確實(shí)很復(fù)雜。但是,事實(shí)上它相當(dāng)簡(jiǎn)單,特別是在你了解 Obj-C 中的指針的情況下。如果要?jiǎng)?chuàng)建一個(gè)UnsafeMutablePointer<Int32> 類(lèi)型的對(duì)象,我們只需要調(diào)用構(gòu)造方法,這個(gè)構(gòu)造方法唯一需要傳入的參數(shù)就是指針的大小(你應(yīng)該知道 C 的指針是不存儲(chǔ)類(lèi)型的,所以它也不會(huì)存儲(chǔ)大小的信息)

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> 構(gòu)造方法傳入的參數(shù)就是需要分配空間的對(duì)象的個(gè)數(shù),所以我們傳入 1 即可,因?yàn)槲覀冎恍枰粋€(gè)Int32 對(duì)象 。然后,只需要把我們之前對(duì) memory 所做的事情反過(guò)來(lái),我們就可以為我們新建的指針賦值。最終,我們只需要簡(jiǎn)單滴把 newIntPtr 傳入到 setCountrPtr 方法中,再把之前 theInt 指針的值打印出來(lái),我們就會(huì)發(fā)現(xiàn)它的值已經(jīng)被更新為 100。

總結(jié)

UnsafeMutablePointer<T> 類(lèi)型的兄弟類(lèi)型 UnsafePointer<T> 從根本上說(shuō)只是 C 指針的一個(gè)抽象。你可以把它們看作 Swift 的可選類(lèi)型,這樣更容易理解。它們不是直接等于一個(gè)確切的值,而是在一個(gè)確切的值上面做了一層抽象。它們的類(lèi)型是泛型,這樣就可以允許其使用其他的值,而不單單是 Int32。比如你需要傳入一個(gè) Float 對(duì)象那么你可能需要 UnsafeMutablePointer<Float>。

重點(diǎn)是:你不是把一個(gè) Int 強(qiáng)轉(zhuǎn)UnsafeMutablePointer<Int>,因?yàn)橹羔槻皇呛?jiǎn)單地一個(gè) Int 值。所以,如果需要?jiǎng)?chuàng)建一個(gè)新的對(duì)象,你需要調(diào)用構(gòu)造方法 UnsafeMutablePointer<Int>(count: Int)

在本文之后我們會(huì)繼續(xù)深入研究函數(shù)指針的一些細(xì)節(jié),然后學(xué)習(xí)如何利用這些特性的優(yōu)勢(shì)去更好地與 C 和 Objective-C 的 API 進(jìn)行交互。一定要注冊(cè)我們的 Newsletter ,這樣你才不會(huì)錯(cuò)過(guò)這些精彩的內(nèi)容!

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)