Swift 3到5.1新特性整理

下面所有的東西,都是來自 hackingwithswift.com

一、swift 5.1

二、swift 5.0

Swift 5.0 最重要的自然是 ABI Stability , 對此可以看這篇 Swift ABI 穩(wěn)定對我們到底意味著什么 。
當(dāng)然還有其他的更新。

1、Result 類型

SE-0235 提議的實現(xiàn)。用來在復(fù)雜對象中的錯誤處理。
Result 類型有兩個帶泛型的枚舉成員 successfailure ,而且 failure 的泛型必須遵循Swift的 Error 類型。

常規(guī)的使用

enum NetworkError: Error {
    case badURL
}
import Foundation

func fetchUnreadCount1(from urlString: String, completionHandler: @escaping (Result<Int, NetworkError>) -> Void)  {
    guard let url = URL(string: urlString) else {
        completionHandler(.failure(.badURL))
        return
    }

    // complicated networking code here
    print("Fetching \(url.absoluteString)...")
    completionHandler(.success(5))
}

fetchUnreadCount1(from: "https://www.hackingwithswift.com") { result in
    switch result {
    case .success(let count):
        print("\(count) unread messages.")
    case .failure(let error):
        print(error.localizedDescription)
    }
}

首先, Result 有個get() 方法,要么返回成功值,要么拋出錯誤。那么可以這么使用。

fetchUnreadCount1(from: "https://www.hackingwithswift.com") { result in
    if let count = try? result.get() {
        print("\(count) unread messages.")
    }
}

再次, Result 可以接受一個閉包來初始化,如果閉包成功返回,就會把它放到 success 的一邊,如果拋出錯誤,就放到 failure 的一邊。

let result = Result { try String(contentsOfFile: someFile) }

最后,你可以使用你自己的錯誤枚舉,但是Swift官方建議,你說用 Swift.Error 來作為 Error 的參數(shù)。

“it’s expected that most uses of Result will use Swift.Error as the Error type argument.”

2、Raw string

SE-0200 引入了,使用 # 來包裹的Raw字符串,里面的字符不會做處理,特別是一些轉(zhuǎn)義字符。

差值需要這樣做

let answer = 42
let dontpanic = #"The answer to life, the universe, and everything is \#(answer)."#

這個對于正則的特別好用

let regex1 = "\\\\[A-Z]+[A-Za-z]+\\.[a-z]+"
let regex2 = #"\\[A-Z]+[A-Za-z]+\.[a-z]+"#

3、自定義字符串插值

SE-0228 提案改進(jìn)了Swift的字符串插值,讓其更高效和自由。

struct User {
    var name: String
    var age: Int
}

extension String.StringInterpolation {
    mutating func appendInterpolation(_ value: User) {
        appendInterpolation("My name is \(value.name) and I'm \(value.age)")
    }
}

let user = User(name: "Guybrush Threepwood", age: 33)
print("User details: \(user)")
// User details: My name is Guybrush Threepwood and I'm 33,

4、動態(tài)可調(diào)用類型

SE-0216 增加了 @dynamicCallable 屬性,來支持方法的動態(tài)調(diào)用,類似 @dynamicMemberLookup 。

你可以將

struct RandomNumberGenerator {
    func generate(numberOfZeroes: Int) -> Double {
        let maximum = pow(10, Double(numberOfZeroes))
        return Double.random(in: 0...maximum)
    }
}

轉(zhuǎn)變?yōu)?/p>

@dynamicCallable
struct RandomNumberGenerator {
    func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double {
        let numberOfZeroes = Double(args.first?.value ?? 0)
        let maximum = pow(10, numberOfZeroes)
        return Double.random(in: 0...maximum)
    }
}

let random = RandomNumberGenerator()
let result = random(numberOfZeroes: 0)
  • @dynamicCallable 參數(shù)
    • 無參數(shù)標(biāo)簽 withArguments ,你可以使用任何遵循 ExpressibleByArrayLiteral 的類型,例如 數(shù)組,數(shù)組切片,set等
    • 有參數(shù)標(biāo)簽的 withKeywordArguments ,使用任何遵循 ExpressibleByDictionaryLiteral 的類型,例如,字典,和key value 對,更多 KeyValuePairs 可以的看這里, 什么是KeyValuePairs?
  • 你可以將其用在結(jié)構(gòu)體,枚舉,類和協(xié)議上
  • 如果你使用 withKeywordArguments 而不是 withArguments ,你仍然按照無參數(shù)標(biāo)簽的方式使用,只是key是空字符串。
  • 如果 withKeywordArguments 或者 withArguments 標(biāo)記為拋出錯誤,調(diào)用類型也會拋出錯誤。
  • 不能在擴展中使用 @dynamicCallable
  • 你仍然可以添加屬性和方法。

5、處理未來的枚舉值

SE_0192 的實現(xiàn)。

有時候枚舉的switch中使用 default 來防治出錯,但不會真正的使用,但是如果未來加了新的 case ,那些處理地方就會遺漏?,F(xiàn)在可以添加 @unknkow 來出觸發(fā)Xcode的提示。

func showNew(error: PasswordError) {
    switch error {
    case .short:
        print("Your password was too short.")
    case .obvious:
        print("Your password was too obvious.")
    @unknown default:
        print("Your password wasn't suitable.")
    }
}

這樣,如果如果代碼中,沒有處理干凈PasswordError ( switch block is no longer exhaustive),就會告警.

6、從 try? 抹平嵌套可選

struct User {
    var id: Int

    init?(id: Int) {
        if id < 1 {
            return nil
        }

        self.id = id
    }

    func getMessages() throws -> String {
        // complicated code here
        return "No messages"
    }
}

let user = User(id: 1)
let messages = try? user?.getMessages()

上面的例子中,Swift 4.2以及之前的, message 會是 String?? , 這樣就不太合理,Swift 5中,就能返回抹平的 String?

7、檢查整數(shù)是否為偶數(shù)

SE-0225 添加了, isMultiple(of:) 來檢查整數(shù)是否為偶數(shù), 和 if rowNumber % 2 == 0 效果一樣。

let rowNumber = 4

if rowNumber.isMultiple(of: 2) {
    print("Even")
} else {
    print("Odd")
}

8、字典compactMapValues() 方法

SE-0218 ,為字典添加了 compactMapValues() 方法,這個就像結(jié)合了,數(shù)組 compactMap() 方法(遍歷成員,判斷可選的值,然后丟棄nil成員)和字典的 mapValues() 方法(只轉(zhuǎn)換字典的value)。

let times = [
    "Hudson": "38",
    "Clarke": "42",
    "Robinson": "35",
    "Hartis": "DNF"
]

let finishers1 = times.compactMapValues { Int($0) }
let finishers2 = times.compactMapValues(Int.init)
let people6 = [
    "Paul": 38,
    "Sophie": 8,
    "Charlotte": 5,
    "William": nil
]

let knownAges = people6.compactMapValues { $0 }
print("compactMapValues, \(finishers1), \(finishers2),\(knownAges)")
// compactMapValues, ["Clarke": 42, "Robinson": 35, "Hudson": 38], ["Robinson": 35, "Clarke": 42, "Hudson": 38],["Charlotte": 5, "Sophie": 8, "Paul": 38]

9、撤回的功能: 帶條件的計數(shù)

SE-0220 , 引入了 count(where:) 函數(shù),來計算遵循 Sequence 列表中滿足條件成員的個數(shù)。

let scores = [100, 80, 85]
let passCount = scores.count { $0 >= 85 }
let pythons = ["Eric Idle", "Graham Chapman", "John Cleese", "Michael Palin", "Terry Gilliam", "Terry Jones"]
let terryCount = pythons.count { $0.hasPrefix("Terry") }

這個功能因為性能問題,被撤回了。

三、Swift 4.2

1、CaseIterable 協(xié)議

SE-0194 提議的實現(xiàn),Swift4.2 增加了 CaseIterable 協(xié)議,能夠給枚舉的 allCases 屬性自動產(chǎn)生所有的枚舉的數(shù)組。

enum Pasta: CaseIterable {
    case cannelloni, fusilli, linguine, tagliatelle
}

for shape in Pasta.allCases {
    print("I like eating \(shape).")
}

當(dāng)然還可以自行實現(xiàn)

enum Car: CaseIterable {
    static var allCases: [Car] {
        return [.ford, .toyota, .jaguar, .bmw, .porsche(convertible: false), .porsche(convertible: true)]
    }

    case ford, toyota, jaguar, bmw
    case porsche(convertible: Bool)
}

2、警告和錯誤指令

SE-0196 提議的實現(xiàn)。Swift 4.2提供這兩個提示,來讓Xcode在編譯時候作出提示

#warning
#error
func encrypt(_ string: String, with password: String) -> String {
    #warning("This is terrible method of encryption")
    return password + String(string.reversed()) + password
}

struct Configuration {
    var apiKey: String {
        #error("Please enter your API key below then delete this line.")
        return "Enter your key here"
    }
}

還可以和#if 配合使用。

#if os(macOS)
#error("MyLibrary is not supported on macOS.")
#endif

3、動態(tài)查找成員

SE-0195 提議的實現(xiàn)。Swift 4.2提供了 @dynamicMemberLookup 的屬性,和 subscript(dynamicMember:) 陪著使用,實現(xiàn)動態(tài)的屬性的取值。

@dynamicMemberLookup
struct Person5 {
    subscript(dynamicMember member: String) -> String {
        let properties = ["name": "Tylor Swift", "city" : "Nashville"]
        return properties[member, default: ""]
    }
 }

let person5 = Person5()
print("person5.name: \(person5.name)")
print("person5.city: \(person5.city)")
print("person5.favoriteIceCream: \(person5.favoriteIceCream)")
// person5.name: Tylor Swift
// person5.city: Nashville
// person5.favoriteIceCream:

當(dāng)然也有類似多態(tài)的用法。

@dynamicMemberLookup
struct Person5 {
    subscript(dynamicMember member: String) -> String {
        let properties = ["name": "Tylor Swift", "city" : "Nashville"]
        return properties[member, default: ""]
    }
    
    subscript(dynamicMember member: String) -> Int {
        let properties = ["age": 26, "height": 178]
        return properties[member, default: 0]
    }
 }

let person5 = Person5()
print("person5.age: \(person5.age)")
let age: Int = person5.age
print("person5.age2: \(age)")
// person5.age: 
// person5.age2: 26

注意你需要指定明確指定類型,Swift才能正確使用。而且如果已經(jīng)有存在屬性,動態(tài)屬性將不會生效

struct Singer {
    public var name = "Justin Bieber"

    subscript(dynamicMember member: String) -> String {
        return "Taylor Swift"
    }
}

let singer = Singer()
print(singer.name)
// Justin Bieber

@dynamicMemberLookup 可以用在協(xié)議,結(jié)構(gòu)體,枚舉,類,甚至標(biāo)注為 @objc 的類,以及它們的繼承者。

例如,陪著協(xié)議的使用,你可以這樣用

@dynamicMemberLookup
protocol Subscripting { }

extension Subscripting {
    subscript(dynamicMember member: String) -> String {
        return "This is coming from the subscript"
    }
}

extension String: Subscripting { }
let str = "Hello, Swift"
print(str.username)

Chris Lattner提議中的例子很有意義,

@dynamicMemberLookup
enum JSON {
   case intValue(Int)
   case stringValue(String)
   case arrayValue(Array<JSON>)
   case dictionaryValue(Dictionary<String, JSON>)

   var stringValue: String? {
      if case .stringValue(let str) = self {
         return str
      }
      return nil
   }

   subscript(index: Int) -> JSON? {
      if case .arrayValue(let arr) = self {
         return index < arr.count ? arr[index] : nil
      }
      return nil
   }

   subscript(key: String) -> JSON? {
      if case .dictionaryValue(let dict) = self {
         return dict[key]
      }
      return nil
   }

   subscript(dynamicMember member: String) -> JSON? {
      if case .dictionaryValue(let dict) = self {
         return dict[member]
      }
      return nil
   }
}

正常使用

let json = JSON.stringValue("Example")
json[0]?["name"]?["first"]?.stringValue

如果用上述的寫法

json[0]?.name?.first?.stringValue

4、有條件地遵循協(xié)議的增強

Swift 4.1引入了有條件地遵循協(xié)議

extension Array: Purchaseable where Element: Purchaseable {
    func buy() {
        for item in self {
            item.buy()
        }
    }
}

但是在Swift 4.1中,如果你要確定對象是否遵循某個協(xié)議,會報錯。Swift 4.2 修復(fù)了這個問題

let items: Any = [Book(), Book(), Book()]

if let books = items as? Purchaseable {
    books.buy()
}

還有,Swift 內(nèi)置的類型,可選,數(shù)組,字典,區(qū)間,如果它們的成員遵循 Hashable ,那么它們也會自動遵循 Hashable

5、隨機數(shù)產(chǎn)生和shuffling

SE-0202 提議的實現(xiàn)。Swift 4.2提供了原生的隨機數(shù)方法。意味著你不需要使用 arc4random_uniform() 或者GameplayKit來實現(xiàn)了。

let randomInt = Int.random(in: 1..<5)
let randomFloat = Float.random(in: 1..<10)
let randomDouble = Double.random(in: 1...100)
let randomCGFloat = CGFloat.random(in: 1...1000)
let randomBool = Bool.random()

SE-0202同樣還提議了 shuffle()shuffled()

var albums = ["Red", "1989", "Reputation"]

// shuffle in place
albums.shuffle()

// get a shuffled array back
let shuffled = albums.shuffled()

還有 randomElement() 方法。

if let random = albums.randomElement() {
    print("The random album is \(random).")
}

6、更簡單,安全的Hash

SE-0206 的實現(xiàn),讓你更簡單的為自建類型使用 Hashable 協(xié)議。

Swift 4.1 能夠為遵循 Hashable 協(xié)議的類型自動生成hash值。但是如果你需要自行實現(xiàn)仍然需要寫不少代碼。

Swift 4.2 引入了 Hasher 結(jié)構(gòu),提供了隨機種子,和通用的hash函數(shù)來簡化過程

struct iPad: Hashable {
    var serialNumber: String
    var capacity: Int

    func hash(into hasher: inout Hasher) {
        hasher.combine(serialNumber)
    }
}

let first = iPad(serialNumber: "12345", capacity: 256)
let second = iPad(serialNumber: "54321", capacity: 512)

var hasher = Hasher()
hasher.combine(first)
hasher.combine(second)
let hash = hasher.finalize()

7、檢查列表是否滿足條件

SE-0207 的實現(xiàn),提供了 allSatisfy() 方法來檢測數(shù)組中所有的元素是否都滿足條件。

let scores = [85, 88, 95, 92]
let passed = scores.allSatisfy { $0 >= 85 }

8、原地字典的元素移除

SE-0197 提供一個全新的 removeAll(where:) 方法,以此來提供一個更高效,會操作原數(shù)據(jù)的類似 filter 的方法。

var pythons = ["John", "Michael", "Graham", "Terry", "Eric", "Terry"]
pythons.removeAll { $0.hasPrefix("Terry") }
print(pythons)

9、Boolean toggling

SE-0199 提供了,對 Booltoggle() 方法,類似

extension Bool {
   mutating func toggle() {
      self = !self
   }
}

Swift 4.2 你可以這樣

var loggedIn = false
loggedIn.toggle()

四、Swift 4.1

1、EquatableHashable 協(xié)議

類和結(jié)構(gòu)體做可比較,需要自己手動實現(xiàn)。

struct Person: Equatable {
    var firstName: String
    var lastName: String
    var age: Int
    var city: String

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName && lhs.age == rhs.age && lhs.city == rhs.city
    }
}

let person1 = Person(firstName: "hicc", lastName: "w", age: 20, city: "shenzhen")
let person2 = Person(firstName: "hicc", lastName: "w", age: 20, city: "shenzhen")
print("person1 1 == person2 : \(person1 == person2)")
// person1 1 == person2 : true

Swift 4.1 提供了 Equatable 的協(xié)議,它會自動的生成 == 方法。
當(dāng)然你還是可以自己實現(xiàn) == 方法(例如,業(yè)務(wù)有id之類的屬性)。

還有之前實現(xiàn)一個對象的hash值也是一件麻煩的事情,你可能需要手動實現(xiàn)類似:

var hashValue: Int {
    return firstName.hashValue ^ lastName.hashValue &* 16777619
}

Swift 4.1 提供了 Hashable 的協(xié)議,可以自動生成 hashValue ,你也還是可以自行實現(xiàn)。

struct Person2: Equatable, Hashable {
    var firstName: String
    var lastName: String
    var age: Int
    var city: String
}

let person11 = Person2(firstName: "hicc", lastName: "w", age: 20, city: "shenzhen")
let person22 = Person2(firstName: "hicc", lastName: "w", age: 20, city: "shenzhen")
print("person11 1 == person22 : \(person11 == person22), \(person11.hashValue)")
// person11 1 == person22 : true, 5419288582170212869

Codable 協(xié)議,Key值轉(zhuǎn)化策略
Swift 4提供了很方便的 Codable 協(xié)議,但是它使用下劃線snake_case而不是駝峰式的方式來轉(zhuǎn)化Key,不太自由。

Swift 4.1 中針對這種情況,提供了 keyDecodingStrategy ,以及 keyEncodingStrategy 屬性(默認(rèn) .useDefaultKeys )來解決這些問題。

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

do {
    let macs = try decoder.decode([Mac].self, from: jsonData)
    print(Macs)
} catch {
    print(error.localizedDescription)
}

let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let encoded = try encoder.encode(someObject)

2、有條件地遵循協(xié)議

Swift 4.1 實現(xiàn)了 SE-0143 的提議,容許你類型在某下情況下才遵循某個協(xié)議。

extension Array: Purchaseable where Element: Purchaseable {
   func buy() {
      for item in self {
         item.buy()
      }
   }
}

這樣會讓你的代碼,更加的安全。如下代碼Swift中會拒絕編譯,因為其未遵循 Coodable 協(xié)議.

import Foundation

struct Person {
   var name = "Taylor"
}

var people = [Person()]
var encoder = JSONEncoder()
try encoder.encode(people)

3、關(guān)聯(lián)類型中的遞歸限制

Swift 4.1實現(xiàn)了 SE-0157 提議,在遞歸協(xié)議中,關(guān)聯(lián)類型可以被定義它的協(xié)議所限制。

protocol Employee {
   associatedtype Manager: Employee
   var manager: Manager? { get set }
}

// TODO: 現(xiàn)在感受不太清楚,后續(xù)有深入了解在補充。

4、canImport 函數(shù)

SE-0075 提議的實現(xiàn)。Swift 4.1引入了 canImport 函數(shù),讓你可以檢查某個模塊能否被導(dǎo)入。

#if canImport(SpriteKit)
   // this will be true for iOS, macOS, tvOS, and watchOS
#else
   // this will be true for other platforms, such as Linux
#endif

之前還有類似的方法

#if !os(Linux)
   // Matches macOS, iOS, watchOS, tvOS, and any other future platforms
#endif

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
   // Matches only Apple platforms, but needs to be kept up to date as new platforms are added
#endif

5、 targetEnvironment 函數(shù)

SE-0190 提議的實現(xiàn),Swift 4.1 提供了 targetEnvironment 函數(shù),來檢測是模擬器還是真實的硬件。

#if targetEnvironment(simulator)
   // code for the simulator here
#else
   // code for real devices here
#endif

6、flatMap改名為compactMap

flatMap之前一個很有用的作用是能夠過濾數(shù)組中為 nil 的元素,Swift 4.2重命名為指意明確,更強大的 compactMap

let array = ["1", "2", "Fish"]
let numbers = array.compactMap { Int($0) }
// [1, 2]

五、Swift 4.0

1、Coodable 協(xié)議

Swift 4之前使用 NSCoding 來做encodingdecoding的事情,但是需要一些模版代碼,也容易出錯,Swift 4中 Coodable 協(xié)議就是為這個而存在。

使用起來簡單到不可思議。

struct Language: Codable {
    var name: String
    var version: Int
}

let swift = Language(name: "Swift", version: 4)

完整的使用

let encoder = JSONEncoder()
if let encoded = try? encoder.encode(swift) {
    if let json = String(data: encoded, encoding: .utf8) {
        print("swift strng\(json)")
    }
    
    let decoder = JSONDecoder()
    if let decoded = try? decoder.decode(Language.self, from: encoded) {
        print("Swift name: \(decoded.name)")
    }
}

2、多行字符串字面量

跨越多行的字符串可以使用 """ 來包裹。

let quotation = """
The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""

3、改進(jìn)Key-value編碼中的 keypaths

keypaths 是指對屬性的引用而不去真正讀取屬性的值。

struct Crew {
    var name: String
    var rank: String
}

struct Starship {
    var name: String
    var maxWarp: Double
    var captain: Crew
}

let janeway = Crew(name: "Kathryn Janeway", rank: "Captain")
let voyager = Starship(name: "Voyager", maxWarp: 9.975, captain: janeway)


let nameKeyPath = \Starship.name
let maxWarpKeyPath = \Starship.maxWarp
let captainName = \Starship.captain.name

let starshipName = voyager[keyPath: nameKeyPath]
let starshipMaxWarp = voyager[keyPath: maxWarpKeyPath]
let starshipCaptain = voyager[keyPath: captainName]

print("starshipName \(starshipName),\(starshipCaptain)")
// starshipName Voyager, Kathryn Janeway

4、改進(jìn)字典函數(shù)

Swift 4改進(jìn)了字典的諸多函數(shù)。

filter
map
mapValues
grouping
default

let cities = ["Shanghai": 24_256_800, "Karachi": 23_500_000, "Beijing": 21_516_000, "Seoul": 9_995_000];
let massiveCities = cities.filter { $0.value > 10_000_000 }
let populations = cities.map { $0.value * 2 }
let roundedCities = cities.mapValues { "\($0 / 1_000_000) million people" }
let groupedCities = Dictionary(grouping: cities.keys) { $0.first! }
let groupedCities2 = Dictionary(grouping: cities.keys) { $0.count }

var favoriteTVShows = ["Red Dwarf", "Blackadder", "Fawlty Towers", "Red Dwarf"]
var favoriteCounts = [String: Int]()

for show in favoriteTVShows {
    favoriteCounts[show, default: 0] += 1
}

print("dic\(massiveCities),\n\(populations),\n\(roundedCities),\n\(groupedCities),\n\(groupedCities2),\n\(favoriteCounts)")
// dic["Shanghai": 24256800, "Beijing": 21516000, "Karachi": 23500000],
// [43032000, 47000000, 19990000, 48513600],
///["Beijing": "21 million people", "Karachi": "23 million people", "Seoul": "9 million people", "Shanghai": "24 million people"],
// ["S": ["Seoul", "Shanghai"], "B": ["Beijing"], "K": ["Karachi"]],
// [8: ["Shanghai"], 5: ["Seoul"], 7: ["Beijing", "Karachi"]],
// ["Blackadder": 1, "Fawlty Towers": 1, "Red Dwarf": 2]

字符串又變成了Collection類型
字符串是Collection類型,這樣就有了諸多便利的方法。

let quote = "It is a truth universally acknowledged that new Swift versions bring new features."
let reversed = quote.reversed()

for letter in quote {
    print(letter)
}

5、單側(cè)區(qū)間

Swift 4 支持了單側(cè)區(qū)間, 缺失的一邊為0或者為集合的盡頭

let characters = ["Dr Horrible", "Captain Hammer", "Penny", "Bad Horse", "Moist"]
let bigParts = characters[..<3]
let smallParts = characters[3...]
print(bigParts)
print(smallParts)
// ["Dr Horrible", "Captain Hammer", "Penny"]
// ["Bad Horse", "Moist"]

六、 Swift 3.1

1、 擴展限制的優(yōu)化

Swift支持對擴展做限制。

extension Collection where Iterator.Element: Comparable {
    func lessThanFirst() -> [Iterator.Element] {
        guard let first = self.first else { return [] }
        return self.filter { $0 < first }
    }
}

let items = [5, 6, 10, 4, 110, 3].lessThanFirst()
print(items)
extension Array where Element: Comparable {
    func lessThanFirst() -> [Element] {
        guard let first = self.first else { return [] }
        return self.filter { $0 < first }
    }
}

let items = [5, 6, 10, 4, 110, 3].lessThanFirst()
print(items)

上述3.0的對擴展的限制都是通過協(xié)議實現(xiàn)。Swift 3.1 支持使用類型來限制。

extension Array where Element == Int {
    func lessThanFirst() -> [Int] {
        guard let first = self.first else { return [] }
        return self.filter { $0 < first }
    }
}

let items = [5, 6, 10, 4, 110, 3].lessThanFirst()
print(items)

2、 嵌套類型支持泛型

Swift 3.1支持了嵌套類型中使用泛型。

struct Message<T> {
    struct Attachment {
        var contents: T
    }

    var title: T
    var attachment: Attachment
}

3、 序列(Sequences)協(xié)議增加了 prefix(while:) , drop(while:) 兩個方法

  • prefix(while:) : 遍歷所有元素,直到遇到不滿足條件的元素 ,并且返回滿足的元素
  • drop(while:) : 就是返回 prefix(while:) 相反的就好。
let names = ["Michael Jackson", "Michael Jordan", "Michael Caine", "Taylor Swift", "Adele Adkins", "Michael Douglas"]
let prefixed = names.prefix { $0.hasPrefix("Michael") }
print(prefixed)
let dropped = names.drop { $0.hasPrefix("Michael") }
print(dropped)

七、Swift 3.0

1、函數(shù)調(diào)用必須使用參數(shù)標(biāo)簽

Swift特點是函數(shù)可以分別制定參數(shù)標(biāo)簽(argument label)和參數(shù)名稱(parameter name)

func someFunction(argumentLabel parameterName: Int) {
}
// 使用必須帶上參數(shù)標(biāo)簽
someFunction(argumentLabel: 1)

// 如果不指定,參數(shù)名稱可以作為菜參數(shù)標(biāo)簽
func someFunction(firstParameterName: Int, secondParameterName: Int) {
}
someFunction(firstParameterName: 1, secondParameterName: 2)

如果你不想使用參數(shù)標(biāo)簽,可以使用 _ 代替

func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
}
someFunction(1, secondParameterName: 2)

2、移除多余代碼

主要是一些內(nèi)置對象,以及和平臺相關(guān)的精簡,讓代碼更加易讀。

// Swift 2.2
let blue = UIColor.blueColor()
let min = numbers.minElement()
attributedString.appendAttributedString(anotherString)
names.insert("Jane", atIndex: 0)
UIDevice.currentDevice()
// Swift 3
let blue = UIColor.blue
let min = numbers.min()
attributedString.append(anotherString)
names.insert("Jane", at: 0)
UIDevice.current

以及

// 第一行是Swift 2.2
// 迪爾汗是Swift 3
"  Hello  ".stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet())
"  Hello  ".trimmingCharacters(in: .whitespacesAndNewlines)

"Taylor".containsString("ayl")
"Taylor".contains("ayl")

"1,2,3,4,5".componentsSeparatedByString(",")
"1,2,3,4,5".components(separatedBy: ",")

myPath.stringByAppendingPathComponent("file.txt")
myPath.appendingPathComponent("file.txt")

"Hello, world".stringByReplacingOccurrencesOfString("Hello", withString: "Goodbye")
"Hello, world".replacingOccurrences(of: "Hello", with: "Goodbye")

"Hello, world".substringFromIndex(7)
"Hello, world".substring(from: 7)

"Hello, world".capitalizedString
"Hello, world".capitalized

以及, lowercaseString -> lowercased()uppercaseString -> uppercased()

dismissViewControllerAnimated(true, completion: nil)
dismiss(animated: true, completion: nil)
dismiss(animated: true)
prepareForSegue()
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?)

3、 枚舉和屬性從大駝峰替換為小駝峰

正如標(biāo)題說的,一方面這是Swift推薦的用法,另外就是內(nèi)置對象的變化

UIInterfaceOrientationMask.Portrait // old
UIInterfaceOrientationMask.portrait // new

NSTextAlignment.Left // old
NSTextAlignment.left // new

SKBlendMode.Replace // old
SKBlendMode.replace // new

還有就是Swift可選類型是通過枚舉來實現(xiàn)的

enum Optional {
    case None
    case Some(Wrapped)
}

如果使用 .Some 來處理可選,也需要更改

for case let .some(datum) in data {
    print(datum)
}

for case let datum? in data {
    print(datum)
}

4、 更swift地改進(jìn)C函數(shù)

大體來說就是讓C函數(shù)使用更加的Swift

// Swift 2.2
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
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()
}

以及

// 第一行是Swift 2.2
// 第二行是Swift 3
CGAffineTransformIdentity
CGAffineTransform.identity

CGAffineTransformMakeScale(2, 2)
CGAffineTransform(scaleX: 2, y: 2)

CGAffineTransformMakeTranslation(128, 128)
CGAffineTransform(translationX: 128, y: 128)

CGAffineTransformMakeRotation(CGFloat(M_PI))
CGAffineTransform(rotationAngle: CGFloat(M_PI))

5、名次和動詞

這部分屬于Swift更加語義化的改進(jìn),到現(xiàn)在5.1的時候一直在改進(jìn),目前[官網(wǎng)最近的規(guī)范]([Swift.org - API Design Guidelines] https://swift.org/documentation/api-design-guidelines/ )方法的部分是:

  • 按照它們的副作用來命名函數(shù)和方法
    • 無副作用的按照名次來命名。 x.distance(to: y) , i.successor()
    • 有副作用的按照動詞來命名。 print(x) , x.sort() , x.append(y)
    • 有修改和無修改命名
      • 動詞的方法中,無修改的使用過去時 ed (通常是,不修改原數(shù)據(jù),而是返回新的),有修改的使用現(xiàn)在時 ing 。
      • 名詞的方法中,無修改的使用名詞,有修改的前面加上 from
Mutating Monmutating
x.sort() z = x.sorted()
x.append(y) z = x.appending(y)
Nonmutating Mutating
x = y.union(z) y.formUnion(z)
j = c.successor(i) c.formSuccessor(&i)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容