深入了解Swift

2021-09-30 11:43:29 字數 3751 閱讀 6745

swift是蘋果新的程式語言,很多人都認為它可以「替換」objective-c,但事實卻並非如此。我花了一些時間對swift執行時和二進位制檔案進行了逆向工程,不過(不過表轉折,可去掉)發現了很多有趣的事。總結起來就是:swift是沒有訊息機制的objective-c。

物件導向

不管你信不信,swift物件實際上是objective-c物件。在mach-o二進位制檔案中可以發現__objc_classlist部分包含了每個類的二進位制資料,結構類似:

struct objc_class ; 

(注:所有的結構都是在64位機器上編譯)

注意資料入口,它指向乙個列出了類的方法、例項變數以及協議等等的結構。通常情況下,資料是8個位元組對齊的。然而,在swift的類中,最後乙個資料將只占有1個位元組。

swift類的實際結構有點奇怪。swift類沒有類似於objective-c的方法,這點會在後面說明。swift類中變數被儲存為乙個例項變數。在swift中利用getter和setter方法修改這個例項變數的值(譯者注:此處的getter和setter並不實際存在,只是抽象為例項變數的訪問和修改入口,是一種規範,在下文中不對getter和setter進行特別翻譯)。奇怪的是,swift類的例項變數其實是沒有型別編碼。通常應該指向型別編碼的指標為null,這想必是因為objective-c的執行時不會親自去處理swift變數。

繼承

正如你所期望的那樣,swift同樣擁有類的繼承與派生。比如,在swift中,shape(形狀)的子類square(方形)在objective-c類中同樣是shape的子類。然而,如果swift中的類沒有父類會怎樣?

例如:

class

shape  

在這種情況下,shape類是乙個swiftobject的子類。而類swiftobject是乙個類似於objective-c中nsobject這樣的乙個基類。它沒有父類,這意味著isa變數指向其本身。其目的是利用swift執行時方法處理像分配和釋放記憶體這樣的事,而不是標準的objective-c執行時。比如說,改為- (void)retain不是呼叫objc_retain,而是呼叫swift_retain

類方法

就像我前面提到的,swift物件的類沒有方法。相反,他們使用像c++一樣的函式、過載以及其他替代。這可能是為什麼swift要比objective-c快得多;這裡不怎麼需要用objc_msgsend來尋找和呼叫方法的實現。

在objective-c中,方法實現類似這樣:

type method(id self, sel _cmd, id arg1, id arg2, ...)

swift方法是非常相似的,但稍有不同的是引數布局,self只能作為最後乙個引數傳遞,這裡沒有選擇器。

type method(id arg1, id arg2, ..., id self)

虛函式表

就像在c++一樣,swift的類有乙個虛函式表,在虛表裡列出了類中的方法。它位於二進位制的資料後面,看起來像這樣:

struct swift_vtable_header  

這裡需要說明的是,swift類的虛函式表只能當它在編譯時可見的情況下使用。否則將會報錯。

name mangling(命名重整)

swift在各自的標誌中儲存元資料的函式(以及更多),這就是所謂的name mangling。該元資料報括函式名,屬性、模組名稱,引數型別,返回型別等等。

class

shape 

以此為例:******descriptio方法改編(過載)的名字是_tfc9swifttest5shape17******descriptionfs0_ft_si。這裡分解下:

t–所有swift符號的字首。所有事情皆始於次。

f–函式。

c一類的函式。(方法)

9swifttest–帶有乙個字首長度的模組名稱。

5shape–函式所屬類的名稱,帶有乙個字首長度。

17******description–函式名。

f–函式屬性。在本例中,它是「f」,這是乙個常見的函式。

s0_ft–我不確定這意味著什麼,但它似乎是標記引數的開始,並返回型別。

「_」該下劃線把引數型別和返回型別分別開來。由於函式沒有引數,所以它直接跟在s0_ft後邊。

s--這是返回型別的開始。「s」代表swift;返回型別是乙個swift內建型別。下乙個字元決定型別。

i–這是swift的內建型別。乙個小寫的「i」,可代替int (小寫的「i」代表int)。

函式屬性

swift的內建命令

命名重整並不僅僅只用於函式,但我只是給出乙個簡短的概述。

函式連線

這句子有足夠的語義,讓我們來到乙個有趣的部分!如果我們有這樣的乙個類:

class

shape  

我們說我們想把numberofsides 改為4。有多種方式來做這個。我們可以使用mobilesubstrate連線到getter方法,並改變返回值,像這樣:

int

(*numberofsides)(id self); 

mshook(int

, numberofsides, id self) 

%ctor 

如果我們建立乙個shape例項,並列印出變數numberofsides的值,我們看到結果為4!這並不是很壞,是吧?現在,我知道你在想什麼;「難道不是應該返回乙個物件,而不是字元「4」嗎?」

的確,在swift中,大量的內建型別都是文字。例如int,與c中的int一樣。注意,字串型別有點奇怪,這是乙個little-endian utf-16字串,所以沒有可以使用的c字面量。

讓我們做同樣的事情,但是這一次,我們會連線setter代替getter。

void

(*setnumberofsides)(

intnewnumber, id self); 

mshook(void

, setnumberofsides, 

intnewnumber, id self) 

%ctor  

再試試跑一次程式吧…結果仍然是5。你一定會問發生什麼事了?這是因為,在swift的某些地方函式是內聯的。該類的建構函式是乙個這樣的函式。它直接設定numberofsides ivar。所以,只有數值在頂部的**中被再次設定時才會呼叫setter方法。執行呼叫後,我們會的到4這個值。

最後,讓我們通過直接設定變數例項改變numberofsides的值。

void

(*setnumberofsides)(

intnewnumber, id self); 

mshook(void

, setnumberofsides, 

intnewnumber, id self) 

%ctor  

這樣同樣可以達到效果。雖然並不推薦這樣做,但它仍是可行的解決方案。

這就是我要寫的所有東西。雖然有很多其他的東西,但我不知道是否有足夠精力去寫。在這篇文章中許多東西是會變化的。它們只是我使用swift從執行時和二進位制檔案逆向工程得到的。但我覺得這還不錯。這意味著,mobilesubstrate不會隨著objective-c消亡,仍有調整的空間。

深入了解A

一 前言 在這裡我將對a 演算法的實際應用進行一定的 並且舉乙個有關a 演算法在最短路徑搜尋的例子。值得注意的是這裡並不對a 的基本的概念作介紹,如果你還對a 演算法不清楚的話,請看姊妹篇 初識a 演算法 這裡所舉的例子是參考amit主頁中的乙個源程式,使用這個源程式時,應該遵守一定的公約。二 a ...

深入了解A

一 前言 在這裡我將對a 演算法的實際應用進行一定的 並且舉乙個有關a 演算法在最短路徑搜尋的例子。值得注意的是這裡並不對a 的基本的概念作介紹,如果你還對a 演算法不清楚的話,請看姊妹篇 初識a 演算法 這裡所舉的例子是參考amit主頁中的乙個源程式,使用這個源程式時,應該遵守一定的公約。二 a ...

深入了解Dojo Data

譯自http www.sitepen.com blog 2010 10 13 dive into dojo data 使用dojo data有助於快速建立web應用的介面,且易於嵌入各種資料來源。它在使用者介面與底層資料之間提供了一層抽象層,使得使用者介面開發人員能夠專注於ui的開發,而無需擔心資料...