Swift 3 0中的函式簽名與函式引用

2021-07-23 04:07:17 字數 4865 閱讀 6725

在swift 3.0中,函式可以通過兩種方式的任一種進行定位:一種是通過函式簽名,還有一種是通過指定具體的函式型別。

什麼是函式簽名?我們知道,swift中函式宣告包括三大部分:函式名,形參列表,返回型別。而形參列表中,每個乙個形參又作為該形參的標籤,這在swift中稱為引數標籤(argument label)。乙個函式的函式名加上其形參的標籤標識即為該函式的簽名,這一點與objective-c的方法很類似。比如,我們看一下以下這組函式:

func myfunc() 

func myfunc(a: int)

func myfunc(value of: float)

這三個函式的函式名都叫myfunc,第乙個函式的引數列表為空,所以,它的函式簽名就是myfunc。第二個函式,它具有乙個形參a,並且該形參名就作為它的引數標籤,所以其函式簽名為myfunc(a:),這裡注意,每個引數標籤後面的冒號不能省,否則就會被swift編譯器認為這是乙個函式呼叫,而不是以函式簽名作為乙個函式物件。第三個函式的形參有自己的乙個形參標籤value,並且該形參的型別為float,其函式簽名為myfunc(value:)。

當我們知道了函式簽名之後,我們在程式中直接就可以將函式簽名作為乙個函式物件進行呼叫,比如:

myfunc(a:)(10)

myfunc(value:)(-0.5)

這裡大家可以看到,乙個函式簽名在swift語句中已經作為乙個標準的函式物件進行使用了,後面直接跟( )即可做函式呼叫。在swift 3.0中,函式物件以及函式物件引用在做實際呼叫過程中不需要再新增引數標籤,並且如果你新增了引數標籤,編譯器反而會報錯。下面我們就來定義指向這三個函式的函式引用物件:

var myfun1: () -> void = myfunc

myfun1()

myfun1 = myfunc as () -> void

myfun1()

let myfun2 = myfunc(a:)

myfun2(100)

let myfun3 = myfunc(value:)

myfun3(0.5)

這段**就出現了本文要描述的關鍵現象!各位注意,這裡myfun1這個函式物件引用要指向不帶任何引數的myfunc時,要麼必須指明該函式物件引用的具體型別,要麼通過as具體指明myfunc函式簽名的具體型別,否則編譯器會報錯——「對myfunc的使用產生歧義」。由於在swift中,光乙個函式名就可以表示該函式物件本身,但如果此函式被過載,那麼光乙個函式名顯然就會對應多個不同的函式實體,因此我們需要使用函式簽名,要麼通過函式具體型別加以區分。

那麼通過函式型別對函式物件加以區分是不是萬能的呢?我們知道,objective-c中方法簽名的強大之處在於,它僅僅通過標籤,而不是通過型別對各個過載的方法進行區分的。在swift中其實在此基礎上又新增了對型別的判別,但是如果碰到引數型別都相同,但簽名不同的情況,通過指明具體型別顯然就會失效!下面我們再增加乙個函式,來看看這道奇觀。

func myfunc() 

func myfunc(a: int)

func myfunc(value of: float)

func myfunc(none: void)

看到myfunc(none:)這個函式各位可能會有些吃驚,當然這個在《the swift programming language》中也是木有提到的,但是對於swift程式語言來說,沒有做不到,只有想不到,只要你腦洞夠大,還能創造更奇葩的寫法~但這個例子能充分說明很多問題了,呵呵。大家沒有看錯,swift可以用void來宣告乙個形參,但要注意,這裡的void其實跟c語言中使用(void)表示乙個空引數列表的形式差不多,在none: void後面是不允許再增加任何形參了,否則編譯即會報錯。但有了標籤,函式簽名自然就會不同,所以這裡的myfunc(none:)與myfunc是不一樣的,並且做直接呼叫的時候,我們這個none標籤還不能省。但這裡先要說的是,當這個函式出現之後,我們上面定義的myfun1立馬就會出現報錯——「對myfunc的使用是有歧義的」。此時,我們暫且把上面定義的myfun1先刪除掉,然後看以下呼叫:

let myfun2 = myfunc(a:)

myfun2(100)

let myfun3 = myfunc(value:)

myfun3(0.5)

let myfun4 = myfunc(none:)

myfun4()

// 直接對myfunc呼叫完全沒問題

myfunc()

// 直接對myfunc(none:)呼叫時,需要加none標籤。

// 同時,乙個空元組即可對應void型別。

// 在swift中void型別本身就是用()空元組來描述的

myfunc(none: ())

因為此時,myfunc與myfunc(none:)的型別是一毛一樣!所以型別再怎麼換都不會好使。而且由於myfunc是木有任何標籤,所以在這裡即便用myfunc(_:)也同樣會報錯!那麼對於函式myfunc()難道就這麼完蛋了?我們無法用乙個函式引用指向它了麼?其實,swift 3.0還藏了一招。既然當這個函式存在過載的情況,那麼我們是否顯式地對它做乙個預設標籤的指示就能讓它復活呢?這個辦法顯然是行之有效的!

func myfunc(_: void) 

func myfunc(a: int)

func myfunc(value of: float)

func myfunc(none: void)

我們對之前的myfunc引數列表中增加_標籤,表示預設。這樣一來,我們就可以這麼玩兒了:

let myfun1 = myfunc(_:)

myfun1()

let myfun2 = myfunc(a:)

myfun2(100)

let myfun3 = myfunc(value:)

myfun3(0.5)

let myfun4 = myfunc(none:)

myfun4()

// 由於標籤是預設的_,所以直接對myfunc呼叫完全沒問題

myfunc()

// 直接對myfunc(none:)呼叫時,需要加none標籤。

// 同時,乙個空元組即可對應void型別。

// 在swift中void型別本身就是用()空元組來描述的

myfunc(none: ())

這樣一來,問題就都解決了。

上面是對於函式的情況,那麼對於類或結構體的成員方法也同樣如此。

class viewcontroller: nsviewcontroller 

func myfunc(a: int)

func myfunc(value of: float)

func myfunc(none: void)

override func viewdidload()

}

上面的self.可省。

最後,我這裡建議各位當你們碰到某乙個函式或方法遇到過載(overload)的情況時,倘若含有無引數的形式,那麼最好用(_: void)的形式指明,這麼做有兩大好處,首先能直接通過函式簽名func(_:)來直接找到它,然後,也是最重要的,可完全避免函式使用歧義的情況發生。

此外,從型別系統上,函式func myfunc(_: void)與func myfunc()的函式型別也是完全一樣的,都是( () ) -> ()。這表示其形參型別為()(即void),返回型別也是()(即void)。

下面給各位提一下在swift 3.0中如何對乙個泛型函式進行引用。在swift程式語言中,對乙個泛型函式進行特化之後必須立馬做呼叫操作,而不能將乙個特化後的函式作為乙個函式引用型別。為了能夠對乙個泛型函式進行使用,我們只能使用as操作符對它做顯式的型別轉換,而不是顯式地對它做特化操作。通過as操作,也能將當前泛型函式的泛型引數做例項化。比如下面**所示:

func foo(param: t) 

// 以下這條語句是錯誤的:

// let ref = foo(param:)

// 我們應該這麼使用泛型函式的引用

let ref = foo(param:) as (int) -> void

ref(100)

func foo(f: t) -> t

let ref2 = foo(f:) as (float) -> float

print("ref2 = \(ref2(9.0))")

let ref3 = foo(f:) as (double) -> double

print("ref3 = \(ref3(10.0))")

// 下面舉乙個更複雜的例子:

var uintobj: uint = 1

let uintptr = withunsafepointer(to: &uintobj)

// 這裡引數中的throws以及返回型別之前的throws都不能省

let ref0 = uintptr.withmemoryrebound(to:capacity:_:) as (int32.type, int, (unsafepointer) throws -> unsafepointer) throws -> unsafepointer// 這裡,對於函式呼叫表示式之後再跟else語句塊的情況下就不能使用trailing closure了

guard let ptr = try? ref0(int32.self, 1, ) else

print("the value is: \(ptr.pointee)")

我們通過這個**例子即能看到,對於乙個泛型函式的引用應該怎麼去做。

Swift3 0 建構函式

viewcontroller.swift import uikit 在swift中,同乙個專案中 同乙個命名空間 所有的類預設都是共享的,不需要引用,可以直接訪問,不需要import 所有物件的屬性 var,也可以直接訪問到 class viewcontroller uiviewcontroller...

關於Swift3 0中的type of 函式

當然,就目前而言,如果我們想在乙個例項方法中方便訪問當前類的類屬性和類方法,那麼可以直接使用type of self 即可,這可能比直接用classname.classmethod 要更通用化一些,或更簡潔一些 如果你的類名比較長的話 下面給出一段 例子 class myclass func met...

swift3 0 中inout關鍵字

swift有兩種引數傳遞方式 1.值傳遞 值傳遞的是引數的乙個副本,這樣在呼叫引數的過程中不會影響原始資料。2.指標傳遞 指標傳遞把引數本身引用 記憶體位址 傳遞過去,在呼叫的過程會影響原始資料。在swift眾多資料型別中,只有class是指標傳遞,其餘的如int,float,bool,charac...