英文水平有限,理解全靠feel。英文原稿:
What's new in Swift 3.0
提示1:
这一次改变非常多,有一些是很小的细节。尽管如此,希望这个改变会使swift在未来变得越来越好,这意味着在以后的版本中改变会变小。
提示2:
如果你没有阅读what's new in Swift 2.2 这个篇文章,你应该知道-一切废弃的方法已经删除,包括:++,—,c语言的循环,元组splat语法,等等
升级到Swift 3时,你会发现基本上每个文件都需要改动!之所以这样,是因为所有的 Cocoa API 名称都改变了。简而言之,API还是原来的 API,但这个 API 在 Objective-C 中是一种叫法,而在 Swift 中是另一种叫法。Swift 3的语法书写起来要更贴近于自然语言。
在Xcode 8中,苹果提供了Migration Assistant,它可以完成大部分的迁移工作。当然,仍然有一部分工作是需要你手动完成的。
API 的改变
Swift 3 中最大的改变是标准库中在每个库中都采用了统一命名方式。API Design Guidleines中包含了这些规则,核心团队在构建 Swift 3 时采用了这些规则,对新手来说,这高度增强了可读性和易用性。核心团队遵循的是"好的 API 设计应当总是从调用者的角度看待问题"的原则。他们努力让 API 简单易用。不再多说,让我们开始介绍这些对你来说非常重要的改变。
省略不必要的单词
在早期的苹果标准库中,方法名中会包含一个单词,用于表明方法的返回值。因为 Swift 编译支持类型推断,这种做法其实并不必要。核心团队尽可能过滤一切"噪音",因此将这些重复的单词都删除了,只留下方法名中最重要的部分。
让我们看看一些简单的例子:
// old way, Swift 2, followed by new way, Swift 3
let blue = UIColor.blueColor()
let blue = UIColor.blue()
let min = numbers.minElement()
let min = numbers.min()
attributedString.appendAttributedString(anotherString)
attributedString.append(anotherString)
names.insert("Jane", atIndex: 0)
names.insert("Jane", at: 0)
UIDevice.currentDevice()
UIDevice.current()
如你所见,这会使得方法名显著的简短!
这个改变对字符串的改变最大,特别是当有重复的时候。证明这一点的最好办法是并排显示之前和之后的代码,所以在下面的代码每一对的第一行是swift2.2,第二个行是swift3.0:
"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
Warning: capitalized 仍然是属性,但是 lowercaseString 和uppercaseString 已经变成方法lowercased() 和 uppercased().
我选择了到目前为止的示例,因为转换到swift3改变不大,但有相当多的变化,非常重要——通常当方法变短,并不是很明显。For example, look at this code:
dismiss(animated: true, completion: nil)
在 Swift 2.2种:
dismissViewControllerAnimated(true, completion: nil)
事实上,completion: nil这个部分是可选的,所以你可以写成这样:
dismiss(animated: true)
类似的变化发生在prepareForSegue(),如下所示:
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?)
在枚举和属性中大写命名被小写取代
虽然和语法无关,我们使用类和结构体、属性、枚举是首字母大写的名字。但是一直遵循一个惯例:类、结构体和枚举使用首字母大写命名(MyStruct, WeatherType.Cloudy),属性和参数的名称使用首字母小写(emailAddress, requestString)。
在swift 3中,属性和参数的名称使用小写命名,比如在swift2.2中创建NSURLRequest使用NSURLRequest(URL: someURL),swift 3中会写成URLRequest(url: someURL),这意味着我们可以使用webView.request?.url?.absoluteString来获取webView 的URL。
以前还有一些不和谐的部分属性大写,比如:CGColor或CIColor,在swift 3中变成了cgColor和ciColor,所以你可以写类似的代码:
let red = UIColor.red.cgColor
另一个和过去的 Swift 代码不同的地方是,在枚举中定义的 case 值现在使用小驼峰命名法。这是为了和属性名或者变量名保持一致
//old way, Swift 2, followed by new way, Swift 3
UIInterfaceOrientationMask.Landscape
UIInterfaceOrientationMask.landscape
NSTextAlignment.Right
NSTextAlignment.right
SKBlendMode.Multiply
SKBlendMode.multiply
UpperCamelCase命名法现在只在类型名和协议名上使用。当你习惯这一切之后,Swift团队对于追求一致性的努力才没有白费。
swift导入C函数
有一些函数是用 C 语言编写的,提供了 C 风格的 API。这个 API 现在在 Swift 中被重新设计
swift 3介绍了C函数属性,在C函数导入swift中使用一些特别优化的方法。比如在swift2中:
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中,CGContext可以视为一个对象,你可以调用它的方法,而不是一遍又一遍的使用CGContext,我们可以这样写:
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()
}
注意:在swift2.2和swift3.0 UIGraphicsGetCurrentContext()都是返回一个可选的CGContext,但是在swift3.0中使用这个方法,我们需要确保可以安全的打开。
还有一些C函数的改变如下:
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))
动词和名词
这部分可能有一些人会开始困惑,这很遗憾,因为这个很重要!下面是一些swift API指南:
标准库中对方法名中使用动词和名词的规定也更加统一。你应当根据这个方法会导致什么后果或者要采取一些动作来进行方法命名。首要原则是如果这个方法名中包含"ed"或"ing"后缀,则表明这是一个名词。方法名为名词的方法有返回值。如果不包含这些后缀,则很可能这是一个动词。以动词命名的方法会对某块引用的内存进行一些操作。即所谓的"修改某个值"。下面是几个符合名词/动词命名规则的方法
customArray.enumerate()
customArray.enumerated()
customArray.reverse()
customArray.reversed()
customArray.sort() // changed from .sortInPlace()
customArray.sorted()
下面是一些使用这些方法的代码片段:
var ages = [21, 10, 2] // 变量,不是常量,这样你才能修改它
ages.sort() // 修改值,现在值变成了 [2, 10, 21]
for (index, age) in ages.enumerated() { // "-ed" 是名词,表示会返回一个 ages 拷贝
print("\(index). \(age)") // 打印:1. 2 \n 2. 10 \n 3. 21
}
第一个参数的 label
在函数或方法中的第一个参数现在必须有一个 label ,除非你显式地声明不要。以前,我们调用一个函数或方法时,可以忽略第一个参数的 label
// old way, Swift 2, followed by new way, Swift 3
"RW".writeToFile("filename", atomically: true, encoding: NSUTF8StringEncoding)
"RW".write(toFile: "filename", atomically: true, encoding: NSUTF8StringEncoding)
SKAction.rotateByAngle(CGFloat(M_PI_2), duration: 10)
SKAction.rotate(byAngle: CGFloat(M_PI_2), duration: 10)
UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
UIFont.preferredFont(forTextStyle: UIFontTextStyleSubheadline)
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
override func numberOfSections(in tableView: UITableView) -> Int
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView?
func viewForZooming(in scrollView: UIScrollView) -> UIView?
NSTimer.scheduledTimerWithTimeInterval(0.35, target: self, selector: #selector(reset), userInfo: nil, repeats: true)
NSTimer.scheduledTimer(timeInterval: 0.35, target: self, selector: #selector(reset), userInfo: nil, repeats: true)
注意,有些方法使用介词"of"、"to"、"with"、"in"作为外部参数名。这是为了增加代码的可读性。
如果这个方法不使用介词也不使用 label,你应该在方法定义时,显式地在第一个参数名之前加一个下划线:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... }
override func didMoveToView(_ view: SKView) { ... }
在许多编程语言中,许多方法可以共用一个方法名,但参数名不同。Swift 也不例外,现在,你可以重载方法,APIs 能够将直接将它们转换成合适的调用。下面是一个例子,展示了 index() 方法的两种重载形式:
let names = ["Anna", "Barbara"]
if let annaIndex = names.index(of: "Anna") {
print("Barbara's position: \(names.index(after: annaIndex))")
}
函数类型
函数在声明和调用时,都需要用括号将参数括住:
func f(a: Int) { ... }
f(5)
但是,当你用函数类型作为参数时,你可能会写出这样的代码:
func g(a: Int -> Int) -> Int -> Int { ... }
// old way, Swift 2
你会发现代码很难读懂。参数在哪里结束,返回值从哪里开始?在 Swift 3 中,正确的定义方法是
func g(a: (Int) -> Int) -> (Int) -> Int { ... }
// new way, Swift 3
现在,参数列表被括号包裹,然后才是返回类型。事情变得简单,同时函数类型更容易被识别出来。通过下面的比照,你会更清楚:
// old way, Swift 2
Int -> Float
String -> Int
T -> U
Int -> Float -> String
// new way, Swift 3
(Int) -> Float
(String) -> Int
(T) -> U
(Int) -> (Float) -> String
Why all this change?
It's easy to read these changes, some of which are tiny but introduce massive breakage, and imagine that Apple's Swift engineers are just out to make our lives harder. However, the truth is that they are working hard to make sure Swift is as easy to learn, easy to use, and fast as possible, which are three very different priorities.
In particular, I have been struck by how committed the Apple team are to ensuring their changes are discussed and agreed in the open, as part of the Swift Evolution community effort. Every change above went through extensive community discussion before being agreed for Swift 3.0, which is an incredible thing to behold.
You can get involved and help shape these changes going forward: they are keen to hear ideas from a wide range of users, and it means the future of Swift really is in your hands.