Swift5.0新特性更新

Swift

  • 原文博客地址: Swift 5.0新特性更新
  • 期待已久的Swift 5.0终于来啦, Swift 5.0Swift中最备受关注的一个版本, 传说中ABI稳定的版本
  • 随着Xcode Bate 10.2的发布, Swift 5.0也发布了测试版, 相信也带来了不少优化和改进
  • 下面运行环境都是在Xcode Bate 10.2环境中进行的

新特性

dynamicCallable

  • SE-0216
  • @dynamicCallableSwift添加了一个新属性, 容许使用一个简单的语法糖调用函数同样调用命名类型
  • 若是须要添加@dynamicCallable属性, 就必需要实现如下方法中的一个或者两个
func dynamicallyCall(withArguments args: [Int]) -> Double

func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double
复制代码
  • 注意点:
  • 除了接受各类输入外,您还能够为各类输出提供多个重载, 自定义返回值, 能够是String, Int等等......
  • KeyValuePairs的使用和介绍, 没有使用过的可参考

下面看一个例子, RandomNumberGenerator生成一个随机数git

Swift 5.0以前的定义和调用方式github

// 定义方式
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属性编程

// 定义方式
@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)
// random(2)等同于random.dynamicallyCall(withArguments: [2])
print(num)
复制代码
  • @dynamicCallable使用注意事项
  • 能够将它应用于结构,枚举,类和协议。
  • 若是你实现withKeywordArguments:而且没有实现withArguments:,你仍然能够在没有参数标签的状况下调用
  • 若是你的实现withKeywordArguments:withArguments:时标记为throw,则调用该类型也将被抛出throw
  • 扩展名没法添加@dynamicCallable,只能添加到主要类型上
  • 仍然能够为你定义的类型添加其余方法和属性,而且可以正常使用

WritableKeyPath

  • SE-0227
  • 添加引用标识键路径的功能,该路径指的是应用它的整个输入值
  • Swift中的每一个值都有一个特殊的伪属性.self,它指的是整个值
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开始, 调用时会报错, 以下swift

enum X {
    // 此处定义切没有调用时不会报错
    case foo(bar: Int...) 
}

func baz() -> X {
    // 此处调用时会报错
    return .foo(bar: 0, 1, 2, 3) 
} 
复制代码

Swift 5以后, 上述定义改为数组参数, 而不是可变参数, 以下api

enum X {
    case foo(bar: [Int]) 
} 

func baz() -> X {
    return .foo(bar: [0, 1, 2, 3]) 
} 
复制代码

Raw Strings

\处理

  • SE-0200增长了建立原始字符串的功能,其中反斜杠和引号被解释为文字符号,而不是转义字符或字符串终止符
  • 单行字符串文字能够用反斜杠填充, 以保证原字符串, 不然会报错
// 文字引用类型
// 错误写法
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”周围的独立引号应该被视为文字引号而不是结束字符串
  • 原始字符串也容许使用反斜杠, 可是将反斜杠视为字符串中的文字字符,而不是转义字符
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), 由于在用#包裹的字符串中反斜杠将会被失败别为文字字符而不是转义字符, 因此必须额外的添加#数组

##处理

  • 在字符串的开头和结尾使用#处理, 在字符串中可使用反斜杠等特殊字符, 那若是字符串中须要使用#, 又该如何处理??
  • 使用#包裹字符串, 默认会以#为字符串的结束符号, #后面的文字将再也不处理, 在这种状况下, 咱们会使用##处理
  • 注意: 字符串的开头和结尾的标识必须同样
let str = ##"My dog said "woof"#gooddog"##

复制代码

多行字符串

原始字符串与Swift的多行字符串系统彻底兼容 - 只需用于#"""启动,而后"""#结束xcode

let multiline = #"""
    The answer to life,
    the universe,
    and everything is \#(answer).
    """#
复制代码

try?嵌套

先看下面代码bash

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"
    }
}
复制代码

Swift4.2及其以前的版本中微信

let user = User(id: 1)
// 这里获得的message的类型是: let messages: String??
let messages = try? user?.getMessages()

// 若是咱们想获得非可选值就须要
print((messages ?? "") ?? "")
// 或者屡次强解, 固然不建议强解写法
print(messages!!)
复制代码
  • Swift4.2及其以前的版本中, 上面返回的是一个2层嵌套的可选值, 若是有多层嵌套处理起来也是至关更麻烦的
  • Swift 5中就完美的解决了这个问题, 若是当前值是可选的, 那么try?将不会将值包装在可选值中, 所以最终结果只是一个String?
  • 所以在Swift 5中不管有多少可嵌套的可选最后, 返回值永远只是一个可选值
  • 一样,若是你使用了可选的连接as?,你仍然只有一个级别的可选性
let user = User(id: 1)
// 类型: let messages: String?
let messages = try? user?.getMessages()

print(messages ?? "")
复制代码

isMultiple

  • SE-0225为整数类型添加了一个方法isMultiple
  • 该方法能够检查一个整数是否为另外一个整数的倍数
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:), 能够获取数组中符合条件的元素的个数
let arr = [1, 2, 34, 5, 6, 7, 8, 12, 45, 6, 9]

let filter = arr.filter({ $0 > 10 })
print(filter)  //[34, 12, 45]

let count = arr.count(where: { $0 > 10 })
print(count)  // 3
复制代码

compactMapValues

  • Swift4.x的版本有两个函数compactMapmapValues
  • compactMap: 返回一个操做后获得的新的数组, 相似flatMap
  • mapValues: 字典中的函数, 对字典的value值执行操做, 返回改变value后的新的字典
let times = [
    "first": 2,
    "second": 43,
    "three": 12,
    "four": 3
]

let compact = times.compactMap({ $0.value > 10 })
print(compact)
// [true, false, true, false]

let mapValues = times.mapValues({ $0 + 2 })
print(mapValues)
// ["second": 45, "first": 4, "three": 14, "four": 5]
复制代码
  • SE-0218Swift 5中新增了一个函数compactMapValues
  • compactMapValues是将上述两个方法的功能合并在一块儿, 返回一个对value操做后的新字典, 而且自动过滤不符合条件的键值对
let times1 = [
    "Hudson": "38",
    "Clarke": "42",
    "Robinson": "35",
    "Hartis": "DNF"
]

let comMap2 = times1.compactMapValues({ Int($0) })
print(comMap2)
// ["Clarke": 42, "Robinson": 35, "Hudson": 38]
复制代码

SubSequence

  • Sequence协议再也不具备SubSequence关联类型。先前返回SubSequenceSequence方法如今会返回具体类型
  • 使用SubSequenceSequence扩展应该修改成相似地使用具体类型,或者修改成Collection的扩展,在CollectionSubSequence仍然可用
// swift 5不在支持
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)
    }
}
复制代码

其余相关更新

SE-0214

DictionaryLiteral类型重命名为KeyValuePairsapp

SE-0238

  • 在使用Swift 5软件包管理器时,Targets能够声明一些经常使用的针对特定目标的build settings设置
  • 新设置也能够基于平台和构建配置进行条件化处理。包含的构建设置支持SwiftC语言定义,C语言头文件搜索路径,连接库和连接框架

SE- 0236

  • 在使用Swift 5时, 能够自定义所支持的最低版本号, 若是该项目的依赖包所支持的最低版本大于项目的最低版本号, 则项目会报错

SR-695

Swift 5中再也不支持返回Self的类方法

// 不在支持
class Base { 
    class func factory() -> Self { /*...*/ }
} 
复制代码

SR-631

不一样文件中的扩展名没法相互识别

class FirstClass { }

extension FirstClass {
    class SecondClass { }
}

// 这里将会报错: "SecondClass is not a member type of FirstClass"
extension FirstClass.SecondClass { 
    class ThirdClass { }
}
复制代码

SR-7251

Swift 5中, 在所声明的类里面, 所声明的变量名不能和类名同样

struct S {}
extension S {
  static var i: Int { return 0 }
  struct i {} // error: “i”的声明无效
}

// 下面的方式是没有问题的
struct S1<T> {}
extension S1 {
  static var i: Int { return 0 }
  struct i {} // This is fine!
}
复制代码

参考文献


欢迎您扫一扫下面的微信公众号,订阅个人博客!

微信公众号
相关文章
相关标签/搜索