函數(shù)參數(shù)和返回值
- 函數(shù)就是這么個(gè)玩意:加一些材料進(jìn)去,通過(guò)一個(gè)過(guò)程胧谈,然后產(chǎn)出一些東西。放進(jìn)去的就是input荸频,輸出來(lái)的就是output菱肖。input就是函數(shù)的參數(shù),output就是函數(shù)的result旭从。
fun sum (_ x: Int, _ y:Int) -> Int {
let result = x + y
return result
}
除非在別的地方調(diào)用函數(shù)蔑滓,傳入特定類(lèi)型的值,否則函數(shù)體的代碼是不會(huì)運(yùn)行的遇绞。沒(méi)傳,傳錯(cuò)類(lèi)型了燎窘,編譯器都會(huì)報(bào)錯(cuò)摹闽。因此在函數(shù)體里,我們就很有信心的使用這個(gè)參數(shù)褐健,知道它是有值的付鹿,并且類(lèi)型也是指定的類(lèi)型。
參數(shù)的名字是本地變量蚜迅。只在函數(shù)體內(nèi)部有效舵匾。
指定了返回值類(lèi)型的函數(shù),就必須要有返回值谁不。并且類(lèi)型也要匹配上坐梯。
return語(yǔ)句實(shí)際上干了兩件事,其一是返回具體的值刹帕。其次是結(jié)束函數(shù)吵血。因此return后面的語(yǔ)句是不執(zhí)行的谎替。
函數(shù)頭的聲明實(shí)際上是以約定。函數(shù)調(diào)用的時(shí)候蹋辅,也遵循這個(gè)約定钱贯。函數(shù)名作為調(diào)用。帶著參數(shù)列表侦另。重要的是類(lèi)型要一致秩命。返回值的類(lèi)型也要一致。
傳入實(shí)參的名字無(wú)所謂褒傅,重要的是它的值弃锐。
函數(shù)調(diào)用的時(shí)候,函數(shù)返回值可以不保存下來(lái)樊卓。當(dāng)然拿愧,編譯器會(huì)給個(gè)警告。如果你實(shí)在不需要這個(gè)返回結(jié)果碌尔,你可以賦值給一個(gè)匿名變量(_)浇辜。或者在前面加上 @discardableResult唾戚。
-
函數(shù)返回值就是函數(shù)的值柳洋,可以直接當(dāng)做返回類(lèi)型使用。只是可讀性差一點(diǎn)叹坦,并且調(diào)試的時(shí)候麻煩點(diǎn)熊镣。但是這樣很常見(jiàn)。
let z = sum(4, sum(5,6))
Void返回類(lèi)型和參數(shù)
-
函數(shù)可以沒(méi)有返回值募书⌒鞔眩可以各種省略。如果沒(méi)有返回值莹捡,則函數(shù)體里面可以沒(méi)有return語(yǔ)句鬼吵。有return的話(huà),僅僅是停止函數(shù)篮赢。
func say1(_ s:String) -> Void {...} func say2(_ s:String) -> () {...} func say3(_ s: String) {...}
函數(shù)也可以沒(méi)有參數(shù)齿椅。寫(xiě)的時(shí)候不用寫(xiě)參數(shù),但是參數(shù)的括號(hào)不能省略启泣。調(diào)用的時(shí)候也不能省略涣脚。
形式上函數(shù)沒(méi)有返回任何東西,實(shí)際上還是返回了Void類(lèi)型的的值寥茫。
函數(shù)簽名
- 函數(shù)簽名表達(dá)式遣蚀,表明函數(shù)的輸入輸出,結(jié)構(gòu)。函數(shù)簽名實(shí)際上就是函數(shù)的類(lèi)型妙同。
- 函數(shù)簽名要包含函數(shù)參數(shù)列表和返回值類(lèi)型射富。即使說(shuō)都是空的。
() -> () //空參數(shù)列表粥帚,返回值也為空
外部參數(shù)名
- 函數(shù)可以外部化參數(shù)名字胰耗。調(diào)用的時(shí)候就需要作為標(biāo)簽標(biāo)示參數(shù)。這樣可以:
- 表明每個(gè)參數(shù)的目的
- 區(qū)分不同的函數(shù)
- 兼容Objective-C和Cocoas
- 這是swift的標(biāo)準(zhǔn)芒涡。默認(rèn)情況下柴灯,所有的參數(shù)都外部化,外部名字和內(nèi)部名字是一樣的费尽。所以一般啥也不用干赠群。
- 如果你想要的和默認(rèn)表現(xiàn)不一樣,有兩種情況
- 修改外部參數(shù)名(相應(yīng)的地方改名字)
- 廢除外部參數(shù)化(用下劃線)
- 外部化的時(shí)候旱幼,調(diào)用的時(shí)候也要把外部化的參數(shù)給到查描。雖然有外部參數(shù),但是參數(shù)的順序是不能變的柏卤。
重載
- swift中重載是可以的冬三,而且非常普遍。函數(shù)簽名不同缘缚,重載是可以的勾笆,因?yàn)閟wift是強(qiáng)類(lèi)型語(yǔ)言,可以區(qū)分不同的函數(shù)桥滨。
- 函數(shù)返回值不同的函數(shù)也可以重載窝爪,同時(shí)存在。但是調(diào)用的時(shí)候必須要能夠明確區(qū)分齐媒。
- 因?yàn)镺bjective-C是不支持重載的蒲每。所以如果你定義了swift的重載函數(shù),而OC又能看見(jiàn)喻括,這是不允許的啃勉。因?yàn)楹蚈C不兼容。
- 有不同外部參數(shù)名的函數(shù)不認(rèn)為是重載双妨。外部參數(shù)名作為函數(shù)名字的一部分,如果外部參數(shù)名不同叮阅,認(rèn)為是不同的函數(shù)刁品。
默認(rèn)參數(shù)值
-
參數(shù)可以有默認(rèn)值。就是說(shuō)如果沒(méi)有傳入實(shí)參浩姥,則可以忽略整個(gè)參數(shù)挑随,用默認(rèn)值。但是不能和函數(shù)重載沖突勒叠。
//定義這樣的形式 func say(_ s: String, times: Int = 1){ for _ in 1...times { print(s) } } //調(diào)用這樣的形式 say("haha")
可變參數(shù)
- 可以用... 放參數(shù)列表中兜挨,表示可變參數(shù)
func sayStrings(_ arrayOfStrings:String ...){
for s in arrayOfStrings{ print(s) }
}
sayStrings("haha","hoho","huhu")
- swift中沒(méi)有把數(shù)組轉(zhuǎn)換成為參數(shù)列表的方法膏孟。
被忽略的參數(shù)
- 參數(shù)內(nèi)參名為下劃線的參數(shù)是被忽略的參數(shù)。這個(gè)參數(shù)在函數(shù)體中沒(méi)有用到拌汇,所以也沒(méi)有名字柒桑。但是,在調(diào)用的時(shí)候依然需要提供這個(gè)參數(shù)噪舀。
- 被忽略的參數(shù)也可以沒(méi)有外部參數(shù)名
- 這么做不是為了讓編譯器滿(mǎn)意魁淳。是為了提醒。
可修改的參數(shù)
- 函數(shù)參數(shù)本質(zhì)上是一個(gè)本地變量与倡,隱含的是let的界逛,即不可修改的。* 當(dāng)然你可以再聲明一個(gè)同名的本地變量纺座,然后把參數(shù)的值傳給這個(gè)本地變量息拜。要想變成可改的:
- 參數(shù)類(lèi)型要定義成inout
- 傳入到里面的參數(shù)必須是var類(lèi)型的,不能是let類(lèi)型的
- 必須以地址的方式傳入
& 符號(hào)顯示的告訴我們净响,傳入的參數(shù)可能有潛在的影響少欺。會(huì)被修改。
- 在和OC交互的時(shí)候别惦,經(jīng)常遇見(jiàn)類(lèi)似UnsafeMutablePointer這樣的東西狈茉,和inout類(lèi)似,都是地址傳遞掸掸。
- 類(lèi)類(lèi)型傳入到函數(shù)內(nèi)部的時(shí)候氯庆,是可以修改類(lèi)內(nèi)部的值的。不用使用inout這樣的標(biāo)示扰付。
- 通常類(lèi)對(duì)象是引用類(lèi)型堤撵,字符串對(duì)象等是值類(lèi)型。
函數(shù)內(nèi)部的函數(shù)
- 函數(shù)的定義可以在任何地方羽莺。包括函數(shù)體內(nèi)部实昨。函數(shù)體內(nèi)部定義的函數(shù),只能在函數(shù)體作用域范圍內(nèi)使用盐固,其他地方看不到荒给。如果以函數(shù)只被另一個(gè)函數(shù)調(diào)用,那么被調(diào)用的那個(gè)函數(shù)就應(yīng)該被包含在調(diào)用函數(shù)里面刁卜。
- 有時(shí)候?yàn)榱舜a的可讀性志电,即使是僅有一個(gè)地方調(diào)用,也可以使用函數(shù)的形式蛔趴。
- 本地函數(shù)不能和在同一個(gè)作用域的本地變量有同樣的名字挑辆。也不能和同一個(gè)作用域的其他函數(shù)沖突。
遞歸
- 函數(shù)調(diào)用函數(shù)本身,就是遞歸鱼蝉。但是要注意遞歸退出條件洒嗤。
函數(shù)作為值
- 函數(shù)是swift中的一等公民。函數(shù)可以用在任何可以傳值的地方
- swift是強(qiáng)類(lèi)型的語(yǔ)言魁亦,賦值需要類(lèi)型一致渔隶。函數(shù)的類(lèi)型就是函數(shù)的簽名。
- 函數(shù)作為值傳遞吉挣,是為了函數(shù)在之后調(diào)用派撕,而不關(guān)心具體的函數(shù)。
- 包含一個(gè)函數(shù)在另一個(gè)函數(shù)里睬魂,可以減少重復(fù)以及發(fā)生錯(cuò)誤的幾率终吼。把變化的東西搞成一個(gè)函數(shù),以參數(shù)的形式傳入即可氯哮。
- 可以用typealiase聲明函數(shù)類(lèi)型际跪。這樣可以更加清晰的使用函數(shù)。畢竟喉钢,函數(shù)簽名的表達(dá)方式太凌亂了姆打。
- Cocoa常常有這樣的函數(shù)傳入,回調(diào)肠虽。OC中的block回調(diào)幔戏。handle。
匿名函數(shù)
- 傳入函數(shù)作為參數(shù)的時(shí)候税课,如果函數(shù)僅僅是在調(diào)用的地方使用闲延,那么函數(shù)名就是沒(méi)必要的了。因此可以直接傳入函數(shù)體韩玩。
- 創(chuàng)建函數(shù)體垒玲。包括大括號(hào)。但是不需要函數(shù)名找颓。
- 函數(shù)參數(shù)和返回值如果有的話(huà)合愈,放在函數(shù)體的第一行。用 in 標(biāo)示击狮。
UIView.animate(withDuration:0.4,
animations :{
()->() in
self.myButton.frame.origin.y += 20
},
completion:{
(finished:Bool) -> () in
print("finished: \(finished)")
}
)
- 匿名函數(shù)還有很多省略的形式
- 如果編譯器知道函數(shù)返回值類(lèi)型佛析,則可以不寫(xiě)箭頭和返回值類(lèi)型。
- 如果沒(méi)有參數(shù)的話(huà)彪蓬,而返回值又可知的話(huà)说莫,可以直接省略in這行。
- 省略參數(shù)類(lèi)型寞焙。如果參數(shù)類(lèi)型編譯器知道,則可以省略。
- 省略括號(hào)捣郊。如果忽略了參數(shù)類(lèi)型辽狈,那么參數(shù)列表的括號(hào)也可以省略。
- 省略in整行呛牲。如果有參數(shù)刮萌,可以直接用$0,$1代替。
- 可以省略參數(shù)名字娘扩。如果參數(shù)沒(méi)有被引用着茸,則可以用下劃線標(biāo)示參數(shù)名。但是你需要告訴編譯器有他們琐旁,也就是說(shuō)涮阔,你可以不寫(xiě)in行直接用$0,$1之類(lèi)的,也可以下劃線in灰殴,但是不能同時(shí)都省略敬特,這樣編譯出錯(cuò)。
- 省略函數(shù)參數(shù)的label牺陶。如果匿名函數(shù)是最后一個(gè)參數(shù)伟阔,可以先寫(xiě)函數(shù)右括號(hào),然后帶著匿名函數(shù)的函數(shù)體在最后掰伸。不寫(xiě)label了皱炉。這叫做trailing function
- 省略調(diào)用函數(shù)的括號(hào)。如果是trailing function的情況狮鸭,并且調(diào)用的函數(shù)除了傳入的函數(shù)之外沒(méi)有別的參數(shù)合搅,那么可以省略函數(shù)調(diào)用時(shí)候的括號(hào)。直接寫(xiě)函數(shù)體怕篷±荩看似函數(shù)定義的形式,其實(shí)是函數(shù)調(diào)用廊谓。這也是函數(shù)調(diào)用唯一可以省略小括號(hào)的地方梳猪。
func doThis(_ f:() ->()){
f()
}
// 忽略括號(hào),這里是函數(shù)調(diào)用蒸痹,不是函數(shù)定義
doThis{
print("Howdy")
}
- 省略關(guān)鍵詞return春弥。如果函數(shù)體只有一行語(yǔ)句并且語(yǔ)句中有值。那么return可以省略叠荠。swift會(huì)假定這個(gè)語(yǔ)句表達(dá)式的值就是返回值匿沛。
可以充分利用匿名函數(shù)的省略特性。同時(shí)你經(jīng)抽欢Γ縮寫(xiě)布局逃呼,這樣更加緊湊鳖孤。
let arr = [2,4,6,8]
func doubleMe(i: Int) -> Int{
return i * 2
}
// 原始的寫(xiě)法
let arr2 = arr.map(doubleMe)
// 更加swift的寫(xiě)法
let arr2 = arr.map({
(i: Int) -> Int in
return i * 2
})
// 非常swift的寫(xiě)法
let arr2 = arr.map{$0 * 2}
定義和調(diào)用
- 可以定義一個(gè)匿名函數(shù)然后立刻調(diào)用它。注意后面的括號(hào)抡笼。
- 為啥有這種玩法苏揣?可以把一塊東西放在一起。而不是比如放在一堆準(zhǔn)備工作的位置推姻。比如造對(duì)象的時(shí)候平匈。
閉包
- swift的函數(shù)就是閉包〔毓牛可以獲取外部的變量到內(nèi)部來(lái)使用增炭。
- 當(dāng)外部環(huán)境變了,依然可以引用到外部變量拧晕。當(dāng)函數(shù)被作為值傳入另一個(gè)函數(shù)的時(shí)候隙姿,也帶著函數(shù)依賴(lài)的周?chē)兞恳黄稹?/li>
閉包怎樣改進(jìn)代碼
- 閉包可以使你的函數(shù)更通用,更有用防症。
// 原本函數(shù)是這樣的
func imageOfSize(_ size:CGSize, _ whatToDraw:()->()) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size,false,0)
whatToDraw()
let result = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return result
}
// 我們可以這樣調(diào)用
let sz = SGSize(width:45,height:20)
let image = imageOfSize(sz) {
let p = UIBesierPath(roundeedRect:sz,cornerRadius:8)
p.stroke()
}
//然后包起來(lái)
func makeRoundedRectangle(_ sz:SGSize) -> UIImage {
let image = imageOfSize(sz){
let p = UIBesierPath(
roundedRect:CGRect(origin:CGPoint.zero,size:sz)
cornerRadius:8
)
p.stroke()
}
}
函數(shù)返回函數(shù)
- (_ sz:CGRect) -> () ->UIImage 類(lèi)似這樣的是說(shuō)返回一個(gè)函數(shù)類(lèi)型孟辑。其中函數(shù)類(lèi)型是()->UIImage
func makeRoundedRectanglemaker(_ sz:CGRect) -> () -> UIImage {
return {
imageOfSize(sz) {
let p = UIBezierPath(
roundedRect:CGRect(origin:CGPoint.zero, size:sz)
cornerRadius:8
)
p.stroke()
}
}
}
閉包設(shè)置一個(gè)捕獲變量
- 閉包捕獲了一個(gè)變量,如果這個(gè)變量可以設(shè)置蔫敲,那么閉包里面可以更改這個(gè)變量饲嗽。
閉包保護(hù)捕獲環(huán)境
- 閉包會(huì)把環(huán)境中的變量保存下來(lái),返回之后如果函數(shù)不釋放奈嘿,那么捕獲的變量也不會(huì)釋放貌虾。
Escaping 閉包
- 如果一個(gè)函數(shù)作為參數(shù)傳遞給另一個(gè)函數(shù),并且延遲執(zhí)行裙犹,這個(gè)函數(shù)參數(shù)必須標(biāo)示成 @escaping尽狠。
- 如果一個(gè)匿名函數(shù)傳了一個(gè)@escaping類(lèi)型的參數(shù),引用了self的函數(shù)或方法叶圃,那么你必須明確指定self袄膏。
柯里化函數(shù)
可以返回帶參數(shù)的函數(shù),外部調(diào)用的時(shí)候掺冠,參數(shù)一個(gè)個(gè)傳下去沉馆。
func makeRoundedRectangleMaker(_ sz:CGSize) -> (CGFloat) -> UIImage {
return {
r in
imageOfSize(sz){
let p = UIBezierPath(
roundedRect:CGRect(origin:CGPoint.zero, size:sz),
cornerRadius:r)
p.stroke()
}
}
}
函數(shù)引用和Selectors
- 函數(shù)引用一般使用函數(shù)名引用,然后可以調(diào)用德崭。也就是簡(jiǎn)單名稱(chēng)斥黑。一般不會(huì)有歧義。
- 在把函數(shù)賦給一個(gè)變量的時(shí)候眉厨,比如如果有重載锌奴,則會(huì)發(fā)現(xiàn)僅僅使用函數(shù)名會(huì)有歧義。所以需要用以下的方法來(lái)處理:
- 使用函數(shù)全名憾股。全名則包括函數(shù)名和外部參數(shù)名鹿蜀。
- 使用函數(shù)簽名箕慧。帶著返回值,輸入輸出類(lèi)型茴恰。有的時(shí)候函數(shù)全部不夠明確销钝,比如沒(méi)有參數(shù)的函數(shù)。全名一樣琐簇。這時(shí)候使用函數(shù)簽名就能指明明確的函數(shù)。
函數(shù)引用范圍
- 有時(shí)候函數(shù)引用可以提供更多關(guān)于函數(shù)在哪里定義的信息座享。這樣告訴編譯器是引用的哪個(gè)函數(shù)婉商。有時(shí)候編譯器會(huì)強(qiáng)制要求你用self來(lái)指示是哪個(gè)函數(shù)。
- 有時(shí)候可以用類(lèi)名+點(diǎn)+方法名來(lái)引用一個(gè)函數(shù)渣叛,即使這個(gè)方法是實(shí)例方法丈秩。
Selectors
- OC中有selector,是方法的引用淳衙。但是這個(gè)東西經(jīng)常引入錯(cuò)誤蘑秽。尤其是需要寫(xiě)正確里面的方法字符串。如果寫(xiě)錯(cuò)了箫攀,就會(huì)在運(yùn)行的時(shí)候告訴你找不到方法肠牲。直接crash。
- 在swift中靴跛,可以用#selector(...)來(lái)代替這種方法缀雳。一方面,編譯器會(huì)檢測(cè)字符串的方法名是不是正確梢睛,如果不正確會(huì)編譯不通過(guò)肥印。另外,編譯器會(huì)幫你生成正確的selector绝葡。
- 偶爾你可能還是要用到oc的方式深碱。
- swift的方式不能保證你絕對(duì)不會(huì)crash。比如你target寫(xiě)錯(cuò)了的時(shí)候藏畅,swift的方式就檢查不出來(lái)敷硅。