期待已久的Swift 5.0
终于来啦, Swift 5.0
是Swift
中最备受关注的一个版本, 传说中ABI
稳定的版本
随着Xcode Bate 10.2
的发布, Swift 5.0
也发布了测试版, 相信也带来了很多优化和改进
下面运行环境都是在Xcode Bate 10.2
环境中进行的
新特性 dynamicCallable
SE-0216
@dynamicCallable
为Swift
添加了一个新属性, 允许使用一个简单的语法糖调用函数一样调用命名类型
如果需要添加@dynamicCallable
属性, 就必须要实现以下方法中的一个或者两个
1 2 3 func dynamicallyCall (withArguments args : [Int ]) -> Double func dynamicallyCall (withKeywordArguments args : KeyValuePairs <String , Int >) -> Double
除了接受各种输入外,您还可以为各种输出提供多个重载, 自定义返回值, 可以是String
, Int
等等……
KeyValuePairs
的使用和介绍, 没有使用过的可参考
下面看一个例子, RandomNumberGenerator
生成一个随机数
1 2 3 4 5 6 7 8 9 10 11 12 13 struct RandomNumberGenerator { func generate (numberOfZeroes : Int ) -> Double { let maximum = pow(10 , Double (numberOfZeroes)) return Double .random(in: 0 ... maximum) } } let random = RandomNumberGenerator ()let num = random.generate(numberOfZeroes: 2 )print (num)
在`Swift 5.0`使用`@dynamicCallable`属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @dynamicCallable struct RandomNumberGenerator { func dynamicallyCall (withArguments args : [Int ]) -> Double { let numberOfZeroes = Double (args.first ?? 0 ) let maximum = pow(10 , numberOfZeroes) return Double .random(in: 0 ... maximum) } } let random = RandomNumberGenerator ()let num = random(2 )print (num)
可以将它应用于结构,枚举,类和协议。
如果你实现withKeywordArguments:
并且没有实现withArguments:
,你仍然可以在没有参数标签的情况下调用
如果你的实现withKeywordArguments:
或withArguments:
时标记为throw
,则调用该类型也将被抛出throw
扩展名无法添加@dynamicCallable
,只能添加到主要类型上
仍然可以为你定义的类型添加其他方法和属性,并且能够正常使用
SE-0227
添加引用标识键路径的功能,该路径指的是应用它的整个输入值
Swift
中的每个值都有一个特殊的伪属性.self,它指的是整个值
1 2 3 4 5 6 7 8 9 10 let id = \Int.self var x = 1 print(id) ////Swift.WritableKeyPath<Swift.Int, Swift.Int> x.self = 2 print(x) //2 print(x.self) //2 print(x[keyPath: id]) //2 x[keyPath: id] = 3 print(x[keyPath: id]) //3
可选参数 在Swift 5
之前,可以编写一个带有可变参数的枚举, 但是在Swift 5
开始, 调用时会报错, 如下
1 2 3 4 5 6 7 8 9 enum X { case foo(bar: Int ...) } func baz () -> X { return .foo(bar: 0 , 1 , 2 , 3 ) }
在Swift 5
之后, 上述定义改成数组参数, 而不是可变参数, 如下
1 2 3 4 5 6 7 enum X { case foo(bar: [Int ]) } func baz () -> X { return .foo(bar: [0 , 1 , 2 , 3 ]) }
Raw Strings \
处理
SE-0200 增加了创建原始字符串的功能,其中反斜杠和引号被解释为文字符号,而不是转义字符或字符串终止符
单行字符串文字可以用反斜杠填充, 以保证原字符串, 否则会报错
1 2 3 4 5 6 7 8 9 10 11 12 let quote = "Alice: " How long is forever? " White Rabbit: " Sometimes , just one second."" let quote1 = "Alice: \" How long is forever?\" White Rabbit: \" Sometimes, just one second.\" " let ucCaseCheck = "enum\s+.+\{.*case\s+[:upper:]" let ucCaseCheck = "enum\\ s+.+\\ {.*case\\ s+[:upper:]"
#
处理
要使用原始字符串, 可使用#
将字符串包裹起来
#
字符串开头和结尾的符号成为字符串分隔符的一部分,因此如下Swift
理解“rain”
和“Spain”
周围的独立引号应该被视为文字引号而不是结束字符串
原始字符串也允许使用反斜杠, 但是将反斜杠视为字符串中的文字字符,而不是转义字符
1 2 3 4 5 6 let rain = #"The "rain" in "Spain" falls mainly on the Spaniards."# let keypaths = #"Swift keypaths such as \Person.name hold uninvoked references to properties."# let answer = 42 let dontpanic = #"The answer to life, the universe, and everything is \#(answer) ."#
上面使用\#(answer)
引用变量而不是\(answer)
, 因为在用#
包裹的字符串中反斜杠将会被失败别为文字字符而不是转义字符, 所以必须额外的添加#
##
处理
在字符串的开头和结尾使用#
处理, 在字符串中可以使用反斜杠等特殊字符, 那如果字符串中需要使用#
, 又该如何处理??
使用#
包裹字符串, 默认会以#
为字符串的结束符号, #
后面的文字将不再处理, 在这种情况下, 我们会使用##
处理
注意: 字符串的开头和结尾的标识必须一样
1 2 let str = ##"My dog said "woof"#gooddog"##
多行字符串 原始字符串与Swift
的多行字符串系统完全兼容 - 只需用于#"""
启动,然后"""#
结束
1 2 3 4 5 let multiline = #""" The answer to life, the universe, and everything is \#(answer) . """#
try?
嵌套先看下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct User { var id: Int init? (id : Int ) { if id < 1 { return nil } self .id = id } func getMessages () throws -> String { return "No messages" } }
在Swift4.2
及其之前的版本中
1 2 3 4 5 6 7 8 let user = User (id: 1 )let messages = try? user? .getMessages()print ((messages ?? "" ) ?? "" )print (messages!! )
在Swift4.2
及其之前的版本中, 上面返回的是一个2层嵌套的可选值, 如果有多层嵌套处理起来也是相当更麻烦的
在Swift 5
中就完美的解决了这个问题, 如果当前值是可选的, 那么try?
将不会将值包装在可选值中, 因此最终结果只是一个String?
因此在Swift 5
中无论有多少可嵌套的可选最后, 返回值永远只是一个可选值
同样,如果你使用了可选的链接as?
,你仍然只有一个级别的可选性
1 2 3 4 5 let user = User (id: 1 )let messages = try? user? .getMessages()print (messages ?? "" )
isMultiple
SE-0225 为整数类型添加了一个方法isMultiple
该方法可以检查一个证书是否为另一个整数的倍数
1 2 3 4 5 6 7 8 9 10 let rowNumber = 4 if rowNumber.isMultiple(of: 2 ) { print ("Even" ) } else { print ("Odd" ) } if rowNumber % 2 == 0 {}
count
SE-0220
在Swift
之前的版本中, 有一个函数filter
可以过滤出数组中符合条件的的元素, 组成一个新的数组, 详细使用可参考Swift函数式编程之高级用法
在Swift 5
中新增了一个函数count(where:)
, 可以获取数组中符合条件的元素的个数
1 2 3 4 5 6 7 let arr = [1 , 2 , 34 , 5 , 6 , 7 , 8 , 12 , 45 , 6 , 9 ]let filter = arr.filter({ $0 > 10 })print (filter) let count = arr.count(where: { $0 > 10 })print (count)
compactMapValues
在Swift4.x
的版本有两个函数compactMap
和mapValues
compactMap
: 返回一个操作后得到的新的数组, 类似flatMap
mapValues
: 字典中的函数, 对字典的value
值执行操作, 返回改变value
后的新的字典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let times = [ "first" : 2 , "second" : 43 , "three" : 12 , "four" : 3 ] let compact = times.compactMap({ $0 .value > 10 })print (compact)let mapValues = times.mapValues({ $0 + 2 })print (mapValues)
SE-0218 在Swift 5
中新增了一个函数compactMapValues
compactMapValues
是将上述两个方法的功能合并在一起, 返回一个对value
操作后的新字典, 并且自动过滤不符合条件的键值对
1 2 3 4 5 6 7 8 9 10 let times1 = [ "Hudson" : "38" , "Clarke" : "42" , "Robinson" : "35" , "Hartis" : "DNF" ] let comMap2 = times1.compactMapValues({ Int ($0 ) })print (comMap2)
SubSequence
Sequence
协议不再具有SubSequence
关联类型。先前返回SubSequence
的Sequence
方法现在会返回具体类型
使用SubSequence
的Sequence
扩展应该修改为类似地使用具体类型,或者修改为Collection
的扩展,在Collection
中SubSequence
仍然可用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 extension Sequence { func dropTwo () -> SubSequence { return self .dropFirst(2 ) } } extension Sequence { func dropTwo () -> DropFirstSequence <Self > { return self .dropFirst(2 ) } } extension Collection { func dropTwo () -> SubSequence { return self .dropFirst(2 ) } }
其他相关更新 DictionaryLiteral
类型重命名为KeyValuePairs
在使用Swift 5
软件包管理器时,Targets
可以声明一些常用的针对特定目标的build settings
设置
新设置也可以基于平台和构建配置进行条件化处理。包含的构建设置支持Swift
和C
语言定义,C
语言头文件搜索路径,链接库和链接框架
在使用Swift 5
时, 可以自定义所支持的最低版本号, 如果该项目的依赖包所支持的最低版本大于项目的最低版本号, 则项目会报错
在Swift 5
中不再支持返回Self
的类方法
1 2 3 4 class Base { class func factory () -> Self { } }
不同文件中的扩展名无法相互识别
1 2 3 4 5 6 7 8 9 10 class FirstClass { }extension FirstClass { class SecondClass { } } extension FirstClass .SecondClass { class ThirdClass { } }
在Swift 5
中, 在所声明的类里面, 所声明的变量名不能和类名一样
1 2 3 4 5 6 7 8 9 10 11 12 struct S {}extension S { static var i: Int { return 0 } struct i {} } struct S1 <T > {}extension S1 { static var i: Int { return 0 } struct i {} }
参考文献