在2016年的WWDC材蛛,swift 3.0隨著iOS 10和Xcode 8一起發(fā)布苇瓣,建議盡早升級swift 3.0剧浸,因為行動越早后面改動越少画机。使用Xcode 8適配swift 3.0的過程中焊刹,你會意識到swift 3.0有很大的改動系任,也許你認為從swift 1.2到2.0已經是很大的改動,我覺得這真的不算什么虐块,因為你還沒有見到swift 3.0的全貌俩滥,它的改動更多更大。
在這邊文章贺奠,我將通過盡量多而全的代碼示例向讀者著重介紹swift 3.0主要改變和優(yōu)化的地方霜旧,并希望在接下來的時間里,對你升級Xcode 8儡率、適配swift 3.0有所幫助挂据。除了以下所列的相對重要的改動,swift 3.0還有其他很多的改動儿普,但是下面所列的改動對于你當前適配swift 3.0的工作會有很大的幫助崎逃。
Notes:
1: swift 3.0有很多很多的改動和優(yōu)化,有些改動可以說瑣碎而又微不足道眉孩,然而這一次swift 3.0的更新對于我們開發(fā)者而言也是一種新的希望个绍,因為這些改動更像是swift誕生兩年以來一次大而徹底的更新,它讓swift更優(yōu)秀更簡潔浪汪,也意味著經過這一次大刀闊斧改動巴柿、優(yōu)化之后,以后的swift版本更新應該將顯著地減少吟宦。
2: 如果你還未閱讀swift 2.2新特性的相關文章篮洁,建議你查閱相關文章,作簡單了解殃姓,swift 2.2中舍棄(deprecated)的語法袁波,例如
++
,--
和C語言風格的for循環(huán)
等語法在swift 3.0中已經被移除了瓦阐。
調用函數(shù)時寫出所有的形參名稱
我們調用函數(shù)的方式在swift 2.0時候已經有過一些改變,這一次swift 3.0更新再一次改變調用函數(shù)的語法篷牌,對之前的swift甚至可以說是一次顛覆睡蟋。在swift 2.x和之前的版本中,調用方法func
不需要顯式寫出第一個參數(shù)名稱枷颊,所以大多數(shù)時候方法第一個參數(shù)名已經包含在方法名稱中了戳杀,看看下面的代碼示例,
// swift 2.x及之前的版本
names.indexOf("Taylor")
"Taylor".writeToFile("filename", atomically: true, encoding: NSUTF8StringEncoding)
SKAction.rotateByAngle(CGFloat(M_PI_2), duration: 10)
UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView?
NSTimer.scheduledTimerWithTimeInterval(0.35, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)
在新版本的swift 3.0中夭苗,方法調用時需要寫出所有的參數(shù)名信卡,當然我們可以在定義一個方法的使用_
下劃線,這也意味著以后調用該方法的時候不需要再寫出參數(shù)名题造,參考下面的代碼傍菇,
struct Person {
func run(name: String) -> Void {
print("\(name) running...")
}
func run(withName name: String) -> Void {
print("\(name) running...")
}
func run(_ name: String) -> Void {
print("\(name) running...")
}
}
現(xiàn)在swift 3.0要求方法調用時候寫出所有的參數(shù)名,下面的代碼比較了swift 2.2和swift 3.0在方法調用時候語法的區(qū)別界赔,
names.indexOf("Taylor")
names.index(of: "Taylor")
"Taylor".writeToFile("filename", atomically: true, encoding: NSUTF8StringEncoding)
"Taylor".write(toFile: "somefile", atomically: true, encoding: String.Encoding.utf8)
SKAction.rotateByAngle(CGFloat(M_PI_2), duration: 10)
SKAction.rotate(byAngle: CGFloat(M_PI_2), duration: 10)
UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
UIFont.preferredFont(forTextStyle: UIFontTextStyle.subheadline)
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
override func numberOfSections(in tableView: UITableView) -> Int
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView?
func viewForZooming(in scrollView: UIScrollView) -> UIView?
NSTimer.scheduledTimerWithTimeInterval(0.35, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)
Timer.scheduledTimer(timeInterval: 0.35, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)
需要注意的是丢习,最后一個例子中,調用方法時淮悼,我們使用Timer
取代了NSTimer
咐低,這是因為在swift 3.0中,SDK中一些基本的類型已經去除了NS
前綴袜腥,例如FileManager
, UserDefaults
, Data
, Date
, URL
, NSURLRequest
, UUID
, NotificationCenter
等见擦。
Swift支持命名空間,這相比Objective-C而言無疑是一種優(yōu)化和進步瞧挤。在swift中锡宋,可以使用命名空間來避免出現(xiàn)命名沖突,只要是來自不同的命名空間特恬,類
class
名字即使相同也不會出現(xiàn)沖突执俩。Swift命名空間的使用不是在一個項目中,而是需要跨項目使用癌刽,在同一個項目中都是同一個命名空間役首,此時全局變量和函數(shù)共享,不需要使用import導入显拜。
大多數(shù)情況下衡奥,建議開發(fā)者調用方法時寫出所有的參數(shù)名,但是一些系統(tǒng)方法又要求遵循以前的調用規(guī)則远荠,即省略第一個參數(shù)名矮固,因為系統(tǒng)方法在定義是使用了_
下劃線,我們來對比一下在swift 2.2和swift 3.0中譬淳,UIKit的一些方法還保持了一致档址,下面是swift 2.2中一些SDK方法的定義盹兢,
// swift 2.2
// 方法定義沒有 _ 下劃線
override func viewWillAppear(animated: Bool)
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
override func didMoveToView(view: SKView)
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?)
func textFieldShouldReturn(textField: UITextField) -> Bool
在swift 3.0中,上述方法在定義時第一個參數(shù)都使用了_
下劃線守伸,這就表明了調用方法時候無需寫出第一個參數(shù)名绎秒,下面是swift 3.0中一些SDK方法的定義,
// swift 3.0
// 為了保持調用與swift 2.2一致尼摹,使用 _ 下劃線定義方法
override func viewWillAppear(_ animated: Bool)
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
override func didMoveToView(_ view: SKView)
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
func textFieldShouldReturn(_ textField: UITextField) -> Bool
刪除多余的詞匯
當swift在2015年12月開源的時候见芹,它的新的簡潔的API設計規(guī)范中包含三個重要的詞語 - Ommit Needless Words
,即刪除多余的詞匯蠢涝。這樣一個API設計規(guī)范給swift 3.0帶來一個另一個顛覆性的改變玄呛,因為它意味著在新的API設計原則下,開發(fā)者在定義swift方法時應該刪除方法名中明確的惠赫,不言而喻的(self-evident)
的詞匯把鉴,先通過下面的代碼看一下swift 2.2 SDK中的方法名故黑,
// swift 2.2
// 方法中包含一些略顯多余的描述性詞匯
let blue = UIColor.blueColor()
let min = numbers.minElement()
attributedString.appendAttributedString(anotherString)
names.insert("Jane", atIndex: 0)
UIDevice.currentDevice()
你能識別上面代碼中多余的詞匯嗎儿咱?當你使用UIColor時,理所當然地场晶,blue就是你想要的顏色混埠,所以使用blueColor()顯得多余;當你添加一段富文本字符串到另一段字符串中诗轻,其實你并不需要通過方法appendAttributedString
詳細地說明該方法是添加富文本字符串钳宪。
那么我們來看一看在swift 3.0中,怎樣刪除方法名中多余的詞匯扳炬,如下代碼所示吏颖,
let blue = UIColor.blue
let min = numbers.min()
attributedString.append(anotherString)
names.insert("Jane", at: 0)
UIDevice.current
如你所見,swift 3.0 SDK中刪除方法中多余的詞匯恨樟,讓方法名的長度明顯變短半醉,語義也更加明確易懂。
這個API設計規(guī)范尤其深刻地影響了字符串(strings)劝术,原先缩多,SDK中的String大多數(shù)方法中包含了大量重復而又多余的描述性詞匯。我們通過下面的代碼來說明swift 2.2和swift 3.0中的String方法名的重大改變吧养晋,
// 刪減字符串空格
"Hello".stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet()) // swift 2.2
"Hello".trimmingCharacters(in: .whitespacesAndNewlines) // swift 3.0
// 包含
"Taylor".containsString("ayl") // swift 2.2
"Taylor".contains("ayl") // swift 3.0
// 以","分割為數(shù)組
"1,2,3,4,5".componentsSeparatedByString(",") // swift 2.2
"1,2,3,4,5".components(separatedBy: ",") // swift 3.0
// 添加文件后綴
myPath.stringByAppendingPathComponent("file.txt") // swift 2.2
myPath.appendingPathComponent("file.txt") // swift 3.0
// 替換
"Hello, world".stringByReplacingOccurrencesOfString("Hello", withString: "Goodbye") // swift 2.2
"Hello, world".replacingOccurrences(of: "Hello", with: "Goodbye") // swift 3.0
// 根據index取子字符串
"Hello, world".substringFromIndex(7) // swift 2.2
"Hello, world".substring(from: 7) // swift 3.0
// 轉換為大寫
"Hello, world".capitalizedString // swift 2.2
"Hello, world".capitalized // swift 3.0
NOTE: capitalized仍然是一個屬性衬吆,String的大小寫轉換在swift 2.2到3.0的更新中,由lowerCaseString, uppercaseString替換為lowercased(), uppercased()绳泉。
本文到此已經列舉了很多的例子逊抡,通過這些例子我想說明的是,在新的API設計規(guī)范下零酪,從swift 2.0到swift 3.0并不是特別巨大的跨越冒嫡。但是在新的swift 3.0 SDK中有相當一部分的方法名有相當大的改變麦射,以至于初次看見這些方法時,我感覺一臉懵逼 - 這也是由于新的API設計規(guī)范所導致的方法名太短灯谣,以至于對于很多開發(fā)者來說潜秋,新的方法名不能見名知意,例如下面的代碼胎许,
dismiss(animated: true, completion: nil)
也許當你第一次看見這樣的語句時峻呛,會感到一臉茫然,也許你心里會想“dismiss什么辜窑?”钩述,這大概是作為iOS開發(fā)者這么長時間以來,自然而然產生的一種斯德哥爾摩綜合征穆碎,但是一旦我們將該方法中刪除的多余詞匯還原,將swift 3.0的語法還原為swift 2.2的語法牙勘,此時你立刻就會明白該方法的作用,如下所示所禀,
// swift 3.0
dismiss(animated: true, completion: nil)
// swift 2.2
dismissViewControllerAnimated(true, completion: nil)
實際上方面,參數(shù)completion: nil
也是可選的,在swift 3.0調用該方法甚至還可以進一步簡寫色徘,如下所示恭金,
dismis(animated: true)
這種簡潔簡短的寫法,可能會帶來一些疑惑褂策,希望開發(fā)者盡快適應横腿。
NOTE: 斯德哥爾摩綜合征,是指犯罪的被害者對于犯罪者產生情感斤寂,甚至反過來幫助犯罪者的一種情結耿焊。這個情感造成被害人對加害人產生好感、依賴心遍搞、甚至協(xié)助加害人罗侯。
在本文,我們將“斯德哥爾摩綜合征”理解為對于之前冗長語法的依賴尾抑,對于新的簡潔的語法的慢接受歇父。
因為從Objective-C時代開始,一直到swift 2.2再愈,在iOS開發(fā)定義方法時榜苫,方法命名一般來說總是很長,所以swift 3.0提倡新的API設計規(guī)范時翎冲,對于大多數(shù)開發(fā)者來說感覺有點突兀垂睬。實際上從長期來說,新的API設計規(guī)范會減輕開發(fā)者的工作,但是由于由于我們長期以來形成的習慣驹饺,對于這些有益的變化钳枕,很難快速接受,反而會更懷念swift 2.2之前赏壹、甚至Objective-C時代那種冗長啰嗦甚至“有害”的方法命名方式鱼炒。
枚舉值和屬性駝峰命名規(guī)則由UpperCamelCase改為lowerCamelCase
為class(類),結構體蝌借,屬性昔瞧,枚舉等命名時,我們幾乎都遵循了這樣的一個約定(follow a convention fairly closely)菩佑,即:class, struct, enum使用首字母大寫UpperCamelCase的駝峰命名規(guī)則自晰,如MyClass, MyStruct, WeatherType.Cloudy;屬性和參數(shù)命名使用首字母小寫lowerCamelCase的駝峰命名規(guī)則稍坯,例如eamilAddress, requestString酬荞。
之所以說“幾乎(fairly closely)”,是因為swift 2.2的一些異常在swift 3.0時將不再是異常瞧哟,在swift 3.0中參數(shù)和屬性命名遵循首字母小寫的駝峰命名規(guī)則混巧。在swift 2.2中,我們通過NSURLRequest(URL: someURL)
來創(chuàng)建NSURLRequest對象绢涡,注意這里的參數(shù)URL
牲剃;而在swift 3.0中,將NSURLRequest初始化方法重寫為NSURLRequest(url: someURL)
雄可,這也意味著開發(fā)者使用webView.request?.url?.absoluteString
這樣的語法來讀取一個webView的URL信息。
上面所說的屬性命名采用首字母小寫的駝峰命名規(guī)則可以應對大多數(shù)情況缠犀,不過也會出現(xiàn)一些違背該原則的情況数苫,一種情況就是屬性名中包含前綴,例如CGColor和CIColor使用CG, CI前綴辨液,在swift 3.0中虐急,將前綴都改為小寫,如下代碼所示滔迈,
let red = UIColor.red.cgColor
這樣的改變有助于保持屬性和參數(shù)命名的一致性止吁,也就是按照swift 3.0提倡的標準,為屬性和參數(shù)命名時燎悍,應該保證首字母小寫敬惦,沒有其他的選擇。
同時谈山,枚舉值的命名標準也由首字母大寫駝峰命名俄删,改為首字母小寫駝峰命名,這樣就與屬性、參數(shù)的命名規(guī)范保持一致畴椰。這其實也是可理解(make sense)的臊诊,可以做一下類比,一個枚舉類型(enum)相當于一個結構體(struct)斜脂,枚舉值(enum values)相當于結構體的屬性(properties)抓艳。在swift 3.0中使用枚舉值,如下代碼所示帚戳,
UIInterfaceOrientationMask.Portrait // old
UIInterfaceOrientationMask.portrait // swift 3.0
NSTextAlignment.Left // old
NSTextAlignment.left // swift 3.0
SKBlendMode.Replace // old
SKBlendMode.replace // swift 3.0
對于開發(fā)者來說壶硅,swift 3.0中,屬性销斟、參數(shù)和枚舉值新的命名規(guī)范并不難理解庐椒,但是這一點小小的改變也帶來了相當大的影響,因為在swift中可選值(Optional)實際上就是SDK定義的枚舉類型蚂踊,如下所示约谈,
// before swift 3.0
enum Optional {
case None
case Some(Wrapped)
}
這意味著如果你之前使用.Some來處理可選值,現(xiàn)在你需要使用.some來替代犁钟。由于通常使用if let來進行解包棱诱,所以我們完全不必要直接使用.some,如下兩種對可選值解包的代碼涝动,效果完全一樣迈勋,
// swift 3.0
for case let .some(datum) in data {
print(datum)
}
for case let datum? in data {
print(datum)
}
可使用swift語法調用C函數(shù)
Swift 3為C函數(shù)引入了新的語法特性,它允許C語言功能庫(library)開發(fā)者通過全新的醋粟、優(yōu)雅的方式將他們的C語言代碼導入swift靡菇。例如,所有已"CGContext"開頭的函數(shù)(functions)在swift 3.0都對應于一個CGContext對象的實例方法米愿,這樣改動之后厦凤,更加符合swift的語法風格。是的育苟,這意味著類似CGContextSetFillColorWithColor
這樣像丑陋的腫瘤一樣的晦澀難懂的語法较鼓,終于從swift開發(fā)中切離了。
為了說明這樣的語法改變违柏,先看一下swift 2.2中調用C函數(shù)的代碼示例博烂,如下所示,
let ctx = UIGraphicsGetCurrentContext()
let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
CGContextSetFillColorWithColor(ctx, UIColor.redColor().CGColor)
CGContextSetStrokeColorWithColor(ctx, UIColor.blackColor().CGColor)
CGContextSetLineWidth(ctx, 10)
CGContextAddRect(ctx, rectangle)
CGContextDrawPath(ctx, .FillStroke)
UIGraphicsEndImageContext()
在swift 3中漱竖,"CGContext"可以被實例化為一個swift對象禽篱,這樣你可以直接調用該對象的方法,而不用一遍一遍地重復CGContext的代碼闲孤。所以谆级,在swift 3烤礁,我們可以重寫之前冗長的代碼,如下所示肥照,
if let ctx = UIGraphicsGetCurrentContext() {
let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
ctx.setFillColor(UIColor.red.cgColor)
ctx.setStrokeColor(UIColor.black.cgColor)
ctx.setLineWidth(10)
ctx.addRect(rectangle)
ctx.drawPath(using: .fillStroke)
UIGraphicsEndImageContext()
}
NOTE: 不管在swift 2.2還是swift 3.0中脚仔,UIGraphicsGetCurrentContext()函數(shù)都返回一個可選值CGContext對象,但由于在swift 3中我們調用了該對象的相關方法舆绎,所以需要對其安全解包之后才能使用鲤脏。
在其他模塊,也可以使用swift語法調用C函數(shù)吕朵,例如你可以讀取CGPDFDocument的numberOfPages屬性猎醇,并且CGAffineTransform模塊由于使用swift語法調用,可讀性和使用效率的提升也相當引人注目努溃。下面是swift舊版本和新版本調用C函數(shù)的代碼硫嘶,
CGAffineTransformIdentity // old
CGAffineTransform.identity // swift 3.0
CGAffineTransformMakeScale(2, 2) // old
CGAffineTransform(scaleX: 2, y: 2) // swift 3.0
CGAffineTransformMakeTranslation(128, 128) // old
CGAffineTransform(translationX: 128, y: 128) // swift 3.0
CGAffineTransformMakeRotation(CGFloat(M_PI)) // old
CGAffineTransform(rotationAngle: CGFloat(M_PI)) // swift 3.0
動詞和名次的命名規(guī)范
這一個模塊的內容可能會讓很多讀者陷入迷惑,過多地解釋這一塊內容也許顯得多余梧税,但這確實很重要沦疾。
NOTE: 由于本人英語能力有限,在翻譯以下內容時第队,感覺太過晦澀哮塞,但是關于命名的規(guī)范還是很重要的,所以我將原文貼出凳谦,本來我在生僻的單詞后面添加了中文備注忆畅,但未免畫蛇添足,希望聰明的讀者們自己理解尸执。
下面的文字內容引用自Swift API guidelines:
"When the operation is naturally described by a verb, use the verb’s imperative for the mutating method and apply the “ed” or “ing” suffix to name its nonmutating counterpart "
"Prefer to name the nonmutating variant using the verb’s past participle"
"When adding “ed” is not grammatical because the verb has a direct object, name the nonmutating variant using the verb’s present participle"
"When the operation is naturally described by a noun, use the noun for the nonmutating method and apply the “form” prefix to name its mutating counterpart"
Got that? It's no surprise that Swift's rules are expressed using lingustic terminology – it is after all a language! – but this at least gives me a chance to feel smug that I did a second degree in English. What it means is that many methods are changing names in subtle and sometimes confusing ways.
依據上述的命名規(guī)范家凯,我們來看一些簡單的例子,
myArray.enumerate() // swift 2.3
myArray.enumerated() // swift 3.0
myArray.reverse() // swift 2.3
myArray.reversed() // swift 3.0
可以看出剔交,swift 3將之前的方法都添加了ed
后綴肆饶,該方法表明這是一個被返回的值。好吧我在這塊翻譯感覺怪怪的岖常,原文是this is a value that's being returned.
,英語渣傷不起啊葫督。
上面的命名規(guī)則足夠清晰明確竭鞍,但是有時它也會讓開發(fā)者感到迷惑,例如當我們處理數(shù)組的排序的時候橄镜。在swift 2.3之前偎快,使用sort()返回一個排序好的新數(shù)組,使用sortInPlace()將原數(shù)組排序洽胶;而在swift 3.0中晒夹,使用sorted()替代sort()裆馒,使用sort()替代sortInPlace(),所以也可以說丐怯,在swift 3.0中喷好,已經沒有sortInPlact()方法了,呃呃呃读跷,好亂梗搅。
這意味著你需要特別的小心,因為在swift 2.3中效览,sort()返回一個排序好的新數(shù)組无切,這是一個新變量;而在swift 3.0中丐枉,表示sort()將當前數(shù)組排序哆键,并不會創(chuàng)建一個新變量。
Swift 3.0為什么要這樣改變
回顧一下文章討論的swift 3.0的改變瘦锹,理解這些改變并不難籍嘹,有些語法規(guī)則的改變看起來很小,但是對于之前版本的swift代碼卻帶來了巨大的破壞沼本,不禁讓我們這些iOS平臺的碼農猜測噩峦,蘋果的swift工程師是不是想讓我們這些碼農生活更加多艱。然而抽兆,事實卻是蘋果的swift工程師正在盡他們的努力來保證swift語言足夠簡單识补,讓我們更容易去學習、使用辫红、讓swift語言更快凭涂,這是蘋果swift工程師優(yōu)先考慮的三個重點。
本人對swift 3.0的一些感想
目前贴妻,本人還在使用swift 2.3版本進行app開發(fā)切油,但工作中也會使用swift 3.0做一些demo,有一些改變確實讓我感覺無所適從名惩,但是在摸索一段時間以后也就感覺得心應手了澎胡,我覺得swift 3.0最讓我滿意的就是方法調用時要傳遞全部參數(shù)這樣一個規(guī)定,這就保證了調用方法和init方法保持了參數(shù)的一致娩鹉;其次攻谁,我比較喜歡的,就是swift 3.0提倡簡潔的語法弯予,將這種約定當做語言的規(guī)范戚宦,讓開發(fā)者在命名方法和變量時,都會力求保證簡潔锈嫩。