翻译Raywenderlich 最新文章What’s New in Swift 4
Swift學習交流群: 313838956 .
本群由Guards翻譯組創建并維護, 志于給認真想學習Swift的同學打造一個良好的交流圈子. 同時群內會不間斷分享Swift的資料, 如果你想學習Swift, 歡迎入群, 但必須滿足以下要求:
該文章轉載翻譯自: Raywenderlich 原文鏈接: What’s New in Swift 4?
####譯者心聲 我們會不定期的更新翻譯文章, 需要可以關注我們的簡書
我們是一群熱愛翻譯并且熱愛 Swift 的人, 希望通過自己的努力讓不熟悉英語的程序員也能學習到國外的高質量的文章. 如發現文章中翻譯錯誤之處, 煩請跟我們聯系, 我們第一時間更正.
想成為我們中一員? 是的, 我們正在招募對翻譯感興趣的小伙伴, 如果你想提高閱讀英語文檔的水平, 如果你不甘寂寞, 如果你想為廣大開發者做點事. QQ: 869293399
Swift 4是蘋果計劃在2017年秋季推出的最新版本,值得關注的是其提供了與Swift 3代碼很好的兼容性,并最大限度的保持了ABI穩定性.
本文著重介紹Swift中可能會明顯影響你的代碼的變化。ok,我們開始吧!
####導讀
在Xcode9中已經集成了Swift4, 你可以從developer portal下載最新版本的Xcode9 (需要開發者賬號), 每個Xcode beta 都會在發布時綁定最新的Swift 4快照版本。
在閱讀時,你會注意到[SE-xxxx]格式的鏈接。這些鏈將帶你進入相關的Swift Evolution提案。如果你想了解更多話題,請務必查看。
我建議你在 Playground上嘗試每個 Swift 4的功能或更新。這將有助于鞏固你的知識,并使你能夠深入并了解每個主題, 試著擴展這些例子!
注意:本文將針對每個Xcode測試版進行更新。如果你使用不同的Swift快照版本,這里的代碼不能保證都有效。 復制代碼####遷移到Swift 4
從Swift 3遷移到4比從2.2到3少了很多麻煩。一般來說,多數變化都是附加的,不需要個人大量去修改。正因為如此,Swift遷移工具可以處理你代碼的大部分變化。
Xcode 9同時支持Swift 4和Swift 3.2的中間版本。項目中的每個target可以是Swift3.2或Swift 4,如果需要,可以只遷移部分。然而, 遷移到Swift3.2并非完全不受限制, 你可能需要更新部分代碼才能與新SDK兼容,且由于Swift ABI尚未穩定,因此你需要使用Xcode 9重新編譯依賴項。
遷移到Swift 4,Xcode還提供了一個遷移工具來幫助你。在Xcode中,你可以點擊導航欄的 Edit/Convert/To Current Swift Syntax… 來啟動轉換工具。
在選擇要轉換的target后,Xcode會提示你優先考慮Objective - C推斷的方式。 選擇推薦的選項,通過限制推斷來減少二進制大小(關于這個主題的更多信息,參閱[Limiting @objc Inference?](https://www.raywenderlich.com/163857/whats-new-swift-4#objc)
###API 變化 為了更好地理解你代碼中的變化,我們首先介紹Swift 4中變更的API
#####Strings String在Swift 4中獲得了很多人的喜愛。 [SE-0163] 這個提案包含很多變化,我們提取出最大的變化的.
字符串已經像之前的2.0版一樣, 改為了Collection類型 。此變化消除了字符串對字符數組的依賴。現在可以直接遍歷字符串對象:
let galaxy = "Milky Way ?"for char in galaxy {print(char)} 復制代碼你不僅可以通過字符串進行邏輯迭代,還可以從Sequence和Collection中獲取所有的bells and whistles :
galaxy.count // 11galaxy.isEmpty // falsegalaxy.dropFirst() // "ilky Way ?"String(galaxy.reversed()) // "? yaW ykliM"// Filter out any none ASCII charactersgalaxy.filter { char inlet isASCII = char.unicodeScalars.reduce(true, { $0 && $1.isASCII })return isASCII} // "Milky Way " 復制代碼上面的ASCII示例演示了對字符的一小部分改進。你現在可以直接從字符訪問UnicodeScalarView。在這之前,你需要實例化一個新字符串[SE - 0178]。
另外還有一個StringProtocol。它聲明了在String上聲明的大部分方法。為了改進slice字符串的效率, Swift 4添加了Substring類型,用于引用String的子序列。
String和Substring都實現了StringProtocol,兩者幾乎具有相同的功能:
// Grab a subsequence of Stringlet endIndex = galaxy.index(galaxy.startIndex, offsetBy: 3)var milkSubstring = galaxy[galaxy.startIndex...endIndex] // "Milk"type(of: milkSubstring) // Substring.Type// Concatenate a String onto a SubstringmilkSubstring += "" // "Milk"// Create a String from a Substringlet milkString = String(milkSubstring) // "Milk"復制代碼另一個很大的改進是String對圖形集合的解析。這個處理方案來自于 Unicode 9的改編。以前,由多個代碼點組成的unicode字符會導致計數大于1。 常見的情況是,這是一個帶有選擇的膚色的表情符號。以下是一個例子:
"???".count // Now: 1, Before: 2"??".count // Now: 1, Before: 2"????????".count // Now: 1, Before, 3復制代碼這只是? String Manifesto 中提到的一個子集, 至于為何這么修改, 可以閱讀你感興趣的修改原始動機和方案。
###Dictionary and Set 至于Collection類型,Set和Dictionary并不那么最直觀的。幸運的是,Swift 團隊對兩者做了很好的改進 [SE-0165] #####Sequence Based Initialization 首先列表可以是從一系列鍵值對(元組)創建一個字典:
let nearestStarNames = ["Proxima Centauri", "Alpha Centauri A", "Alpha Centauri B", "Barnard's Star", "Wolf 359"]let nearestStarDistances = [4.24, 4.37, 4.37, 5.96, 7.78]// Dictionary from sequence of keys-valueslet starDistanceDict = Dictionary(uniqueKeysWithValues: zip(nearestStarNames, nearestStarDistances)) // ["Wolf 359": 7.78, "Alpha Centauri B": 4.37, "Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Barnard's Star": 5.96]復制代碼#####Duplicate Key Resolution 在初始化Dictionary時, 你現在可以使用你喜歡的方式來處理重復的鍵,同時避免覆蓋鍵值對,且不會出現任何問題:
// Random vote of people's favorite starslet favoriteStarVotes = ["Alpha Centauri A", "Wolf 359", "Alpha Centauri A", "Barnard's Star"]// Merging keys with closure for conflictslet mergedKeysAndValues = Dictionary(zip(favoriteStarVotes, repeatElement(1, count: favoriteStarVotes.count)), uniquingKeysWith: +) // ["Barnard's Star": 1, "Alpha Centauri A": 2, "Wolf 359": 1]復制代碼上面的代碼使用了 zip 和 + 來快捷地處理重復的 key 和沖突的值。
注意:如果你不熟悉 zip,你可以在Apple的Swift文檔中快速了解它 [Swift Documentation] 復制代碼#####Filtering Dictionary 和 Set現在都可以將結果 通過filter函數 過濾到原始類型的新對象中:
// Filtering results into dictionary rather than array of tupleslet closeStars = starDistanceDict.filter { $0.value < 5.0 }closeStars // Dictionary: ["Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Alpha Centauri B": 4.37] 復制代碼#####Dictionary Mapping Dictionary為直接映射其值提供了一種非常有用的方法::
// Mapping values directly resulting in a dictionarylet mappedCloseStars = closeStars.mapValues { "\($0)" }mappedCloseStars // ["Proxima Centauri": "4.24", "Alpha Centauri A": "4.37", "Alpha Centauri B": "4.37"] 復制代碼#####Dictionary Default Values 在Dictionary上訪問某個值時,常見的做法是使用nil-coalescing operator給出默認值(譯者注: 不了解nil-coalescing operator 的伙伴 可參見Nil-Coalescing Operator。Dictionary Default Values可以更簡潔:
// Subscript with a default valuelet siriusDistance = mappedCloseStars["Wolf 359", default: "unknown"] // "unknown"// Subscript with a default value used for mutatingvar starWordsCount: [String: Int] = [:]for starName in nearestStarNames {let numWords = starName.split(separator: " ").countstarWordsCount[starName, default: 0] += numWords // Amazing }starWordsCount // ["Wolf 359": 2, "Alpha Centauri B": 3, "Proxima Centauri": 2, "Alpha Centauri A": 3, "Barnard's Star": 2] 復制代碼swift4之前, 處理這種情況需要包裝在臃腫的if - let語句中。現在簡短的代碼即可.
#####Dictionary Grouping 另一個令人驚訝稱贊的是,我們可以從Sequence"中初始化Dictionary,并將其分組為bucket::
// Grouping sequences by computed keylet starsByFirstLetter = Dictionary(grouping: nearestStarNames) { $0.first! }// ["B": ["Barnard's Star"], "A": ["Alpha Centauri A", "Alpha Centauri B"], "W": ["Wolf 359"], "P": ["Proxima Centauri"]]復制代碼當通過特定模式對數據進行分組時,這相當方便。
#####預留空間 Sequence和Dictionary現在都具有明確保留容量的能力。
// Improved Set/Dictionary capacity reservationstarWordsCount.capacity // 6starWordsCount.reserveCapacity(20) // reserves at _least_ 20 elements of capacitystarWordsCount.capacity // 24復制代碼在這些類型上,Reallocation可能是一項代價高昂的任務。如果你知道需要存儲的數據量時, 使用reserveCapacity(_:)可以提高性能且便捷
#####私有訪問修飾符 Swift3中,有些人不太喜歡使用fileprivate。理論上它很強大,但在實踐中,它的用法常令人困惑。其目的是在成員內部實現私有,不同一文件中,訪問公有成員變量的時候很少使用fileprivate。
問題是Swift鼓勵使用extension將代碼分解成邏輯組。extension 代碼在原始作用域之外,所以fileprivate也被廣泛使用。
Swift 4通過在類型和類型的extension間, 實現共享相同的訪問范圍。但這只適用于相同的源文件
struct SpaceCraft {private let warpCode: Stringinit(warpCode: String) {self.warpCode = warpCode}}extension SpaceCraft {func goToWarpSpeed(warpCode: String) {if warpCode == self.warpCode { // Error in Swift 3 unless warpCode is fileprivateprint("Do it Scotty!")}}}let enterprise = SpaceCraft(warpCode: "KirkIsCool")//enterprise.warpCode // error: 'warpCode' is inaccessible due to 'private' protection levelenterprise.goToWarpSpeed(warpCode: "KirkIsCool") // "Do it Scotty!"復制代碼####新增API 現在讓我們來看看Swift 4的新特性。這些更改不應該破壞現有的代碼,因為它們只是附加的。
#####存檔和序列化 目前為止,要序列化和歸檔你的自定義類型,你必須跳過一些坑。對于類類型,你需要將NSObject子類化并實現NSCoding協議。像struct和enum這樣的值類型需要一些技巧,例如創建一個可以擴展NSObject和NSCoding的子對象。 Swift 4解決了這一問題,三種類型均可以序列化?[SE-0166]:
struct CuriosityLog: Codable {enum Discovery: String, Codable {case rock, water, martian}var sol: Intvar discoveries: [Discovery] }// Create a log entry for Mars sol 42 let logSol42 = CuriosityLog(sol: 42, discoveries: [.rock, .rock, .rock, .rock]) 復制代碼在這個示例中,你可以看到,要使swift類型 Encodable和 Decodable,惟一的要求是實現Codable的協議。如果所有的屬性都是Codable的,編譯器自動生成該協議的相應實現
要真正對對象進行編碼,需要將其傳遞給Encoder。 每個編碼器根據不同的方案對你的對象進行編碼?[SE-0167](Note: 部分提議仍在完善中):
let jsonEncoder = JSONEncoder() // One currently available encoder// Encode the datalet jsonData = try jsonEncoder.encode(logSol42)// Create a String from the datalet jsonString = String(data: jsonData, encoding: .utf8) // " {"sol":42,"discoveries":["rock","rock","rock","rock"]}" 復制代碼它將一個對象自動編碼為JSON對象。一定要檢查JSONEncoder公開的屬性,以定制它的輸出。 該過程的最后一部分是將數據解碼為具體對象:
let jsonDecoder = JSONDecoder() // Pair decoder to JSONEncoder// Attempt to decode the data to a CuriosityLog object let decodedLog = try jsonDecoder.decode(CuriosityLog.self, from: jsonData) decodedLog.sol // 42 decodedLog.discoveries // [rock, rock, rock, rock]復制代碼使用Swift 4的 encoding/decoding,你可以在不依賴@ objc協議的開銷和限制的情況下獲得Swift的類型安全性。
#####鍵值編碼 到目前為止,你可以在不調用函數的情況下對函數進行引用,因為在Swift中函數實質是一個閉包。對于Swift 4, 可以引用類型的keyPath來獲取或設置實例的底層值?[SE-0161]:
struct Lightsaber {enum Color {case blue, green, red}let color: Color }class ForceUser {var name: Stringvar lightsaber: Lightsabervar master: ForceUser?init(name: String, lightsaber: Lightsaber, master: ForceUser? = nil) {self.name = nameself.lightsaber = lightsaberself.master = master} }let sidious = ForceUser(name: "Darth Sidious", lightsaber: Lightsaber(color: .red)) let obiwan = ForceUser(name: "Obi-Wan Kenobi", lightsaber: Lightsaber(color: .blue)) let anakin = ForceUser(name: "Anakin Skywalker", lightsaber: Lightsaber(color: .blue), master: obiwan)復制代碼在這里,你創建了一些ForceUser實例,通過設置他們的name、lightsaber和master。要創建一個關鍵路徑,你只需使用一個反斜杠\,后面跟上你感興趣的屬性:
// Create reference to the ForceUser.name key path let nameKeyPath = \ForceUser.name// Access the value from key path on instance let obiwanName = obiwan[keyPath: nameKeyPath] // "Obi-Wan Kenobi" 復制代碼在這個實例中,你給ForceUser的name屬性創建了一個keyPath。然后,你可以傳遞給新的subscript keyPath來使用這個keyPath。現在,默認情況下,此下標現在可用于每種類型 下面是一些使用keyPath來深入到子對象、設置屬性和構建keyPath引用的例子:
// Use keypath directly inline and to drill down to sub objects let anakinSaberColor = anakin[keyPath: \ForceUser.lightsaber.color] // blue// Access a property on the object returned by key path let masterKeyPath = \ForceUser.master let anakinMasterName = anakin[keyPath: masterKeyPath]?.name // "Obi-Wan Kenobi"// Change Anakin to the dark side using key path as a setter anakin[keyPath: masterKeyPath] = sidious anakin.master?.name // Darth Sidious// Note: not currently working, but works in some situations // Append a key path to an existing path //let masterNameKeyPath = masterKeyPath.appending(path: \ForceUser.name) //anakin[keyPath: masterKeyPath] // "Darth Sidious" 復制代碼Swift的keyPath之美是強類型的! 沒有更多的objective - c字符串混亂風格!
###多行String
多行String是很多編程語言非常常見的特性。Swift4添加了這個非常簡單和有用的語法是通過三個引號來實現的 ?[SE-0168]:
let star = "??" let introString = """A long time ago in a galaxy far,far away....You could write multi-lined stringswithout "escaping" single quotes.The indentation of the closing quotesbelow deside where the text linebegins.You can even dynamically add valuesfrom properties: \(star)""" print(introString) // prints the string exactly as written above with the value of star復制代碼當創建XML/JSON消息的時候或者創建UI界面上非常長的格式化字符串的時候這是非常有用的。
###半開區間
為了減少冗長和提高可讀性,標準庫現在可以推導出開始和結束使用半開區間。?[SE-0172]
這為確定集合的范圍的起始索引或者結束索引提供了非常便利的方法。
// Collection Subscript var planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"] let outsideAsteroidBelt = planets[4...] // Before: planets[4..<planets.endIndex] let firstThree = planets[..<4] // Before: planets[planets.startIndex..<4] 復制代碼正如你所看到的,半開區間減少了明確的開始或者結束索引。
#####無窮大 他們也允許在開始索引確定的情況下定義無窮的的結束索引。
// Infinite range: 1...infinity var numberedPlanets = Array(zip(1..., planets)) print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (8, "Neptune")]planets.append("Pluto") numberedPlanets = Array(zip(1..., planets)) print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (9, “Pluto")]復制代碼#####模式匹配 另一個非常大的作用是將半開區間用于模式匹配:
// Pattern matchingfunc temperature(planetNumber: Int) {switch planetNumber {case ...2: // anything less than or equal to 2print("Too hot")case 4...: // anything greater than or equal to 4print("Too cold")default:print("Justtttt right")} }temperature(planetNumber: 3) // Earth 復制代碼###泛型下標 下標是高效訪問數據類型的方式, 為了提高通用性 添加了泛型。[SE-0148]
struct GenericDictionary<Key: Hashable, Value> {private var data: [Key: Value]init(data: [Key: Value]) {self.data = data}subscript<T>(key: Key) -> T? {return data[key] as? T} }復制代碼在上面這個例子中,返回值類型是泛型。你可以使用泛型下標就像這樣:
// Dictionary of type: [String: Any] var earthData = GenericDictionary(data: ["name": "Earth", "population": 7500000000, "moons": 1])// Automatically infers return type without "as? String" let name: String? = earthData["name"]// Automatically infers return type without "as? Int" let population: Int? = earthData["population"]復制代碼不僅僅只是返回值類型可以是泛型,實際下標類型也可以是泛型:
extension GenericDictionary {subscript<Keys: Sequence>(keys: Keys) -> [Value] where Keys.Iterator.Element == Key {var values: [Value] = []for key in keys {if let value = data[key] {values.append(value)}}return values} }// Array subscript value let nameAndMoons = earthData[["moons", "name"]] // [1, "Earth"] // Set subscript value let nameAndMoons2 = earthData[Set(["moons", "name"])] // [1, "Earth"]復制代碼在這個例子中,你可以看到,傳遞兩個不同的Sequence類型(Array和Set)會得到各自的數組值。
###其他
以上列舉的囊括了swift4中最大變化的部分, 現在我們快速看看其他方面的小改動 #####MutableCollection.swapAt(::) MutableCollection現在具有mutate方法swapAt(_:_ :); 交換指定的索引值[SE-0173]:
// Very basic bubble sort with an in-place swap func bubbleSort<T: Comparable>(_ array: [T]) -> [T] {var sortedArray = arrayfor i in 0..<sortedArray.count - 1 {for j in 1..<sortedArray.count {if sortedArray[j-1] > sortedArray[j] {sortedArray.swapAt(j-1, j) // New MutableCollection method}}}return sortedArray }bubbleSort([4, 3, 2, 1, 0]) // [0, 1, 2, 3, 4] 復制代碼#####關聯類型約束 現在可以使用where語句來約束關聯類型[SE-0142]:
protocol MyProtocol {associatedtype Elementassociatedtype SubSequence : Sequence where SubSequence.Iterator.Element == Iterator.Element } 復制代碼通過協議,在associatedtype的聲明時就可以直接約束它的值。
#####類和協議的存在方式 在定義某個類型時(譯者注: 指變量或常量), 這特性可以讓其必須符合某種類型以及遵守一組協議[SE-0156]:
protocol MyProtocol { } class View { } class ViewSubclass: View, MyProtocol { }class MyClass {var delegate: (View & MyProtocol)? }let myClass = MyClass() //myClass.delegate = View() // error: cannot assign value of type 'View' to type '(View & MyProtocol)?' myClass.delegate = ViewSubclass() 復制代碼#####限制@objc 推斷 要向objective - c公開或使用Swift API,可以使用@ objc編譯器特性。在很多情況下,編譯器可以為快速推斷。然而, 這種大量的推理會導致三個問題是:
Swift 4通過限制@objc的推斷來解決這個問題[SE-0160] ,也就是說在Objective-C處于完全動態的情況下,您需要使用@objc。 您需要修改的幾個示例程序包括私有方法,動態聲明和NSObject子類的任何方法。
#####NSNumber Bridging NSNumber和Swift的數字之間有許多有趣的行為,這些行為長久以來一直困擾著這個語言。幸運的是,Swift 4將這些可以避免這些錯誤[SE-0170]。 這里有一個例子
let n = NSNumber(value: 999) let v = n as? UInt8 // Swift 4: nil, Swift 3: 231 復制代碼在Swift 3中會出現的怪異現象,如果數字溢出,它會從0開始。在這個例子中"999% 2 ^ 8 = 231。 只有在包含的類型中可以安全地表示這個數字時,Swift 4才通過強制轉換返回一個值 #####Swift Package Manager
在最近幾個月中,Swift Package Manager有許多更新,幾個比較大的更新如下: · 源代碼的依賴來自于分支或者提交的分支 · 更容易控制的package版本 · 用更為常見的解決方案代替非直觀的命令 · 能夠自定義編譯的swift版本 · 指定每個target的源文件的位置
這些都是SPM所做的重大改變。 SPM還有很長的路要走,但是我們可以通過保持積極的建議來幫助它更完善。 有關最近解決的提案的全面了解,請查看Swift 4 Package Manager Update。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的翻译Raywenderlich 最新文章What’s New in Swift 4的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: node.js下载安装并配置WebStr
- 下一篇: 围棋人机大战明日上演,这份观赛指南请留好