• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Swift 3到5.1新特性整理

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

本文转载自:https://hicc.me/whats-new-in-swift-3-to-5-1/,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有。

Hipo 2.0 重写从 Swift 1的版本写到2的版本,后续Hipo功能稳定,更新慢了很多……,Swift本身却在长足的发展,5.0都已经发布了,本文对Swift 3.0 到Swift 5.1 的更新点做个总结。

为了方便阅读,准备从新到旧的总结。

下面所有的东西,都是来自 hackingwithswift.com 。

Swift 5.1

// TODO:

Swift 5.0

Swift 5.0 最重要的自然是 ABI Stability , 对此可以看这篇 Swift ABI 稳定对我们到底意味着什么 。

当然还有其他的更新。

Result 类型

SE-0235 提议的实现。用来在复杂对象中的错误处理。

Result 类型有两个带泛型的枚举成员 success 和 failure ,而且 failure 的泛型必须遵循Swift的 Error 类型。

常规的使用

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 的参数。

its expected that most uses of Result will use Swift.Error as the Error type argument.”

Raw string

SE-0200 引入了,使用 # 来包裹的Raw字符串,里面的字符不会做处理,特别是一些转义字符。

差值需要这样做

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]+"#

自定义字符串插值

SE-0228 提案改进了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,

// TODO: 更多使用,需要多研究

动态可调用类型

SE-0216 增加了 @dynamicCallable 属性,来支持方法的动态调用,类似 @dynamicMemberLookup 。

你可以将

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

转变为

@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 参数
    • 无参数标签 withArguments ,你可以使用任何遵循 ExpressibleByArrayLiteral 的类型,例如 数组,数组切片,set等
    • 有参数标签的 withKeywordArguments ,使用任何遵循 ExpressibleByDictionaryLiteral 的类型,例如,字典,和key value 对,更多 KeyValuePairs 可以的看这里, 什么是KeyValuePairs?
  • 你可以将其用在结构体,枚举,类和协议上
  • 如果你使用 withKeywordArguments 而不是 withArguments ,你仍然按照无参数标签的方式使用,只是key是空字符串。
  • 如果 withKeywordArguments 或者 withArguments 标记为抛出错误,调用类型也会抛出错误。
  • 不能在扩展中使用 @dynamicCallable
  • 你仍然可以添加属性和方法。

处理未来的枚举值

SE_0192 的实现。

有时候枚举的switch中使用 default 来防治出错,但不会真正的使用,但是如果未来加了新的 case ,那些处理地方就会遗漏。现在可以添加 @unknkow 来出触发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),就会告警.

从 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?

检查整数是否为偶数

SE-0225 添加了, isMultiple(of:) 来检查整数是否为偶数, 和 if rowNumber % 2 == 0 效果一样。

let rowNumber = 4

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

字典 compactMapValues() 方法

SE-0218 ,为字典添加了 compactMapValues() 方法,这个就像结合了,数组 compactMap() 方法(遍历成员,判断可选的值,然后丢弃nil成员)和字典的 mapValues() 方法(只转换字典的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]

撤回的功能: 带条件的计数

SE-0220 , 引入了 count(where:) 函数,来计算遵循 Sequence 列表中满足条件成员的个数。

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

CaseIterable 协议

SE-0194 提议的实现,Swift4.2 增加了 CaseIterable 协议,能够给枚举的 allCases 属性自动产生所有的枚举的数组。

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

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

当然还可以自行实现

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)
}

警告和错误指令

SE-0196 提议的实现。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

动态查找成员

SE-0195 提议的实现。Swift 4.2提供了 @dynamicMemberLookup 的属性,和 subscript(dynamicMember:) 陪着使用,实现动态的属性的取值。

@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:

当然也有类似多态的用法。

@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才能正确使用。

而且如果已经有存在属性,动态属性将不会生效

struct Singer {
    public var name = "Justin Bieber"

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

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

@dynamicMemberLookup 可以用在协议,结构体,枚举,类,甚至标注为 @objc 的类,以及它们的继承者。

例如,陪着协议的使用,你可以这样用

@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

有条件地遵循协议的增强

Swift 4.1引入了有条件地遵循协议

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

但是在Swift 4.1中,如果你要确定对象是否遵循某个协议,会报错。Swift 4.2 修复了这个问题

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

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

还有,Swift 内置的类型,可选,数组,字典,区间,如果它们的成员遵循 Hashable ,那么它们也会自动遵循 Hashable 。

随机数产生和 shuffling

SE-0202 提议的实现。Swift 4.2提供了原生的随机数方法。意味着你不需要使用 arc4random_uniform() 或者GameplayKit来实现了。

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).")
}

更简单,安全的Hash

SE-0206 的实现,让你更简单的为自建类型使用 Hashable 协议。

Swift 4.1 能够为遵循 Hashable 协议的类型自动生成hash值。但是如果你需要自行实现仍然需要写不少代码。

Swift 4.2 引入了 Hasher 结构,提供了随机种子,和通用的hash函数来简化过程

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()

检查列表是否满足条件

SE-0207 的实现,提供了 allSatisfy() 方法来检测数组中所有的元素是否都满足条件。

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

原地字典的元素移除

SE-0197 提供一个全新的 removeAll(where:) 方法,以此来提供一个更高效,会操作原数据的类似 filter 的方法。

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

                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap