What's New in Swift 3
SE-0002: Removing currying function declaration syntax
1 | //Before: |
在 Swift 3 之前定义柯里化函数需要使用两个括号,好消息是现在可以这样写了:
1 | //After |
函数变得更加直观:传入一个 Int
类型的 x,并返回一个接受 String
类型并返回 Float
类型的函数。
SE-0042: Flattening the function type of unapplied method references
假设我们有一个叫 Type 的 struct
类型,其中包含 instanceMethod 方法。
1 | struct Type { |
对 instanceMethod 进行赋值时,会显示 f 的类型为 (Type) -> (Int) -> Int
1 | let f = Type.instanceMethod //(Type) -> (Int) -> Int |
对应的调用方式:
1 | f(Type(x: 1))(2) // ==> 3 |
这个提案将 f 的类型 (Type) -> (Int) -> Int
flatten 成了 (Type, y: Int) -> Int
,这样 f 的调用语法更像一个标准的 func
:
1 | let f = Type.instanceMethod // f: (Type, y: Int) -> Int |
SE-0049: Move @noescape and @autoclosure to be type attributes
假设现在有一个叫 f2
的 func
,接受一个带有 @autoclosure
关键字的参数:
1 | func f2(@autoclosure a : () -> ()) {} |
可以这样调用 f2
:
1 | f2(print("hello”)) |
或者将这个 f2
赋值给 x
(注:函数是 Swift 中的一等公民,这里 x
的类型是 (@autoclosure () -> ()) -> ()
):
1 | let x = f2 |
但如果你强制将 x
定义为其他类型时,编译器会报错:
1 | let y : Int = x |
但是奇怪的是,如果你显式地定义一个变量的类型,编译器会报错:
1 | let x2 : ( () -> ()) -> () = f2 |
对比上面 x
和 x2
,编译器的行为自相矛盾了。
因此,这个提案将原本用于定义参数的 @noescape
和 @autoclosure
关键字改为定义类型。
1 | // Before |
SE-0035: Limiting inout capture to @noescape contexts
在 Swift 中,@noescape
关键字能够保证闭包在被调用的函数返回前就已经执行。
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape. [^noescape]
这份提案将 inout
关键字捕获的内容限制在 @noescape
中。
注:参数标记为 inout
,意味着它的值是要被修改并且最后被函数返回的。
因此,在 Swift 3 中,如果你在 @escape
闭包中试图改变一个 inout
的参数,编译器会报错:
1 | func escape(f: () -> ()) {} |
SE-0103: Make non-escaping closures the default
现在闭包默认添加关键字 @noescape
:
To provide some more details, this approach has the following advantages:
Most functional algorithms written in pure Swift will benefit because they are naturally noescape. The core team feels that this will reduce the boilerplate involved with writing these algorithms.
The compiler has enough logic in it to provide a great QoI experience when a developer doesn’t think about escaping, and tries to escape a closure - it can provide a fixit that suggests adding @escaping.
Recent changes (to disallow escaping closures to close over an inout parameter) are pushing the language to prefer noescape closures. noescape closures have also always been the preferred default, since they eliminate a class of retain cycle issues.
“@autoclosure(escaping)” can be simplified and standardized to “@autoclosure @escaping”
@noescape
关键字将会在 Swift 3 中被移除,同时,编译器在发现作为参数的闭包在可能逃逸出函数体时会报错,提示你应该为参数添加 @escaping
关键字。
SE-0066: Standardize function type argument syntax to require parentheses
如果你再看回 #1,你就会发现柯理化函数是这样定义的: func curried(x: Int) -> (String) -> Float
。
而这正是这个提案所修改的地方。
1 | // Before |
作为参数的函数类型都要加上圆括号以表示这是一个函数参数。
类似地:
1 | (Int) -> Int // 传入一个 Int |
SE-0047: Defaulting non-Void functions on unused results
考虑下面的代码:
1 | func f() -> T {} |
当调用 f
时,编译器提示 f()
的返回值没有使用。
你可以将 f
赋值给下划线 _
来消除 warning:
1 | func f() -> T {} |
或者在这个函数前面添加 @discardableResult
。
1 | @discardableResult func f() -> T {} |
SE-0102 Remove @noreturn attribute and introduce an empty Never type
@noreturn
关键字被移除,并加入全新的 Never
枚举类型,定义如下:
The return type of functions that do not return normally; a type with no
values.Use
Never
as the return type when declaring a closure, function, or
method that unconditionally throws an error, traps, or otherwise does
not terminate.func crashAndBurn() -> Never { fatalError("Something very, very bad happened") }
因此,抛出异常可以这些写:
1 | func noReturn() -> Never { |
SE-0054: Abolish implicitly unwrapped optional type
1 | let x: Int! = 5 |
在 Swift 3,y 的类型会变成 Int?
,一个 optional Int,因为我们没有强制将它 unwrap。但 z 的类型为 Int
,因为我们在做加法前已经强制把它 unwrap 成 Int
了。
更详细的例子:
1 | func f() -> Int! { return 3 } // f: () -> Int?, has IUO attribute |
接下来这几个 proposal 都和 Objective-C 有关系:
SE-0111: Remove type system significance of function argument labels
SE-0111 是关于定义函数参数时下划线的讨论。
SE-0021: Naming functions with argument labels
SE-0021 是关于函数命名风格的讨论。
SE-0022: Referencing the Objective-C selector of a method
Swift 3 中你可以通过使用 #selector
来操作 selector,就像 Objective-C 中的 @selector
和 SEL
一样。
1 | extension UIView { |
SE-0064: Referencing the Objective-C selector of property getters and setters
现在可以通过 #selector
为一个类的属性指定 setter 和 getter 方法。
- 你需要使用 dynamic 修饰符,因为需要调用和 Objective-C 里相同的运行时特性。
- 你需要将属性定义为
var
变量,因为需要通过#selector
进行 overrides。
1 | class Person: NSObject { |
SE-0062: Referencing Objective-C key-paths
跟 #13 类似,现在可以用 #keyPath
来指定 key path 来获取属性。
1 | extension UIView { |
SE-0004: Remove the ++ and – operators
关于将 ++
,--
替换为 += 1
,-= 1
的讨论。
SE-0007: Remove C-style for-loops with conditions and incrementors
C 风格的 for 循环从此消失:
1 | for var i = 0 ; i < 10 ; i++ { |
Swift 风格的循环如约而至。
1 | for i in 0 ..< 10 { |
SE-0065: A new model for collections and indices
以往,我们可以通过先获取 collections
类型的 index,并通过 index 的 successor()
来获得下一个元素。
Swift 3 中,collections
类型将负责遍历器的遍历。
1 | // Before |
更多有关 Collection
类型的提案:
- Adding a public base property to slices (SE-0093)
- Constraining AnySequence.init (SE-0014)
- Add prefix(while:) and drop(while:) to the stdlib (SE-0045)
- Add sequence(first:next:) and sequence(state:next:) to the stdlib (SE-0094)
SE-0032: Add first(where:) method to Sequence ()
我们经常会在 Collection
或 Sequence
中查找第一个满足某种特定条件的元素,Swift 3 中为 Sequence
类型添加了一个 first(where:)
的内置方法。
从此,你再也不用写一些类似 seq.filter(predicate).first
或 seq.lazy.filter(predicate).first
的代码。
SE-0008: Add a lazy flatMap for sequences of optionals
为 Sequence
添加 lazy 版本的 flatMap 方法。
SE-0052: Change IteratorType post-nil guarantee
当迭代器类型开始返回 nil 时,后面的结果都会返回 nil。
SE-0025: Scooped access level
Swift 3 加入新的访问级别:fileprivate
,只能在当前文件内可以访问对应的 API。
Open access and public access enable entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use open or public access when specifying the public interface to a framework. The difference between open and public access is described below.
Internal access enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure.
File-private access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
Private access restricts the use of an entity to the enclosing declaration. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
Open
> Internal(default)
> fileprivate
> private
SE-0070: Make optional requirements Objective-C only
在 Objective-C 中,协议方法有两种:分别是 require 和 optional。在 Swift 3 中,你也可以为一个协议方法添加 optional
关键字:前提是 protocol 和 func 前都有 @objc
标志。
1 | protocol NSTableViewDelegate { |
SE-0011: Replace typealias keyword with associatedtype for associated type declarations
SE-0092: Typealiases in protocols and protocol extensions
typealias
在以前的版本中有两个用处,一个是用在 protocol 中,还有一个是用在 extension 中。
1 | // Before |
第二个 typealias
将 Container.Generator.Element
的名字用 Element
代替。 而 protocol 里面所用的 typealias
,其实并不是一个真正意义上的 typealias ,它只是一个我们稍后在 protocol 里面用到的东西,并知道它会遵从 SequenceType。这个 Container
应该是一个 associatedtype
,而不是一个严格意义上的 typealias。
Swift 3 中加入了 associatedtype
关键字更明确地区分开这两种用法。
1 | // After |
SE-0048: Generic type aliases
你可以将范型用在 typealias
上,使类型定义更具表达性。
1 | typealias StringDictionary<T> = Dictionary<String, T> |
SE-0069: Mutability and foundation value types
关于 Mutability 和值类型的讨论。
SE-0086: Drop NS prefix in swift foundation
带有历史包袱的 NS
前缀终于在 Swift 3 移除了,开发者的大事,大快所有人心的大好事。
更多相关提案:
- Fully eliminate implicit bridging conversions from Swift (SE-0072)
- Remove the Boolean protocol (SE-0109)
SE-0112: Improved NSError bridging
还记得 Swift 2 加入的 Error Handling (try
和 catch
,throws
和 rethrows
,throw
和 do
) 吗?
这个提案就是关于桥接 ErrorProtocol
和 NSError
的讨论,Swift 3 改善了 NSError
的错误处理,并加入新的 LocalizedError
和 RecoverableError
类型。
1 | protocol LocalizedError : Error { |
相关链接:
- SE-0002 - Removing currying func declaration syntax
- SE-0004 - Remove the ++ and – operators
- SE-0007 - Remove C-style for-loops with conditions and incrementers
- SE-0008 - Add a Lazy flatMap for Sequences of Optionals
- SE-0011 - Replace typealias keyword with associatedtype for associated type declarations
- SE-0021 - Naming Functions with Argument Labels
- SE-0022 - Referencing the Objective-C selector of a method
- SE-0025 - Scoped Access Level
- SE-0032 - Add first(where:) method to Sequence
- SE-0035 - Limiting inout capture to @noescape contexts
- SE-0042 - Flattening the function type of unapplied method references
- SE-0047 - Defaulting non-Void functions so they warn on unused results
- SE-0048 - Generic Type Aliases
- SE-0049 - Move @noescape and @autoclosure to be type attributes
- SE-0052 - Change IteratorType post-nil guarantee
- SE-0054 - Abolish ImplicitlyUnwrappedOptional type
- SE-0062 - Referencing Objective-C key-paths
- SE-0064 - Referencing the Objective-C selector of property getters and setters
- SE-0065 - A New Model for Collections and Indices
- SE-0066 - Standardize function type argument syntax to require parentheses
- SE-0069 - Mutability and Foundation Value Types
- SE-0070 - Make Optional Requirements Objective-C-only
- SE-0086 - Drop NS Prefix in Swift Foundation
- SE-0092 - Typealiases in protocols and protocol extensions
- SE-0102 - Remove @noreturn attribute and introduce an empty Never type
- SE-0103 - Make non-escaping closures the default
- SE-0111 - Remove type system significance of function argument labels
- SE-0112 - Improved NSError Bridging