swift print 格式化中怎么样打出表情 我想用print("[高兴]") 这样就条出一个高兴的表情

Swift 在对 Objective-C 改进的 6 个方面 - 技术翻译 - 开源中国社区
当前访客身份:游客 [
已有文章 2413 篇
当前位置:
Swift 在对 Objective-C 改进的 6 个方面
英文原文:
0人收藏此文章,
推荐于 2年前 (共 9 段, 翻译完成于 06-15) ()
参与翻译(2人):
在 Atomic Object 的安娜堡办公室,我们做了一个观看2014年的WWDC主题演讲,毫不夸张地说,当Swift宣布的时候,我们感到十分激动。Swift,苹果正在推进的一个更现代的编程语言。我很高兴能获得先机并开始用它开发。&
在这篇博文中,我将重点介绍Swift的几个语言特性,这将使开发者的工作更加轻松。
&翻译的不错哦!
Objective-C语言的问题
当一个开发者申请一个关于Atomic Object的新职位时,我们会给他填写一个(GTKY)&Getting To Know You的表。这个表要求填写一些常见的问题,包括技术和其他方面,比如你最喜欢的语言是什么,你会做些什么来改进它?很多开发者的回答不能令人满意,即使开发人员选择objective-C作为他们最喜爱的语言时,也想不出如何改进它! 这个回答产生了大量的讨论,让我想起一个Objective-C的问题。仅举几例:
弱类型 - 通常处理id或class,并且需要可怕的C static casts 。
欠佳的枚举语法 - for in已经很好了,但我经常想到一个更好的索引。
缺少操作符重载的类,例如NSNumber的。
所以,我很高兴苹果宣布swift并公开了其语言细节。在这里我会谈一些对Objective-C语言的批评。我强烈建议开发人员,看一看在Xcode 6的测试版。
&翻译的不错哦!
激动人心的Swift特性
1.&类型推断
对我来说这个很有用。无比烦恼的是你不得不将id类型的实例非安全的转化为实际类型,这很容易出错并且向一个实际对象并不相应的id发送信号。总是检查respondsToSelector太乏味了。Swift给我们提供了一个解决方案---var关键字:
1&&var&anInt&=&0
&&anInt:&Int&=&0
2&&var&aDouble&=&0.0
&&aDouble:&Double&=&0
3&&var&anotherDouble:&Double&=&0
&&anotherDouble:&Double&=&0
4&&var&aString&=&"some&string"
&&aString:&String&=&"some&string"
注意:就像anotherDouble那种情况,你也可以显示的设置类型。可以用var同样的方式使用let来推断常量的类型。
&翻译的不错哦!
2.&改进枚举语法
Objective-C有不错的集合枚举语法:
for&(SomeType&someObject&in&array)
&&&&NSLog(@"%@",&someObject.whatever);
这是个不错的语法糖,但是问题是现实编程中,我通常需要一个和集合中元素实例一起的索引变量。.
Swift 给了我们许多,&但是尤其令人高兴的是他们提供了一个全局的enumerate()方法,可以用来返回一个包含了数组中元素和索引的元组。
$R2:&String[]&=&size=4&{
&&[0]&=&"one"
&&[1]&=&"two"
&&[2]&=&"three"
&&[3]&=&"four"
20&&for&(index,&value)&in&enumerate(array)&{
21.&&&&&println("Item&\(index):&\(value)")
&&Item&0:&one
&&Item&1:&two
&&Item&2:&three
&&Item&3:&four
&翻译的不错哦!
3. Override关键字
当在类中声明一个实例函数时,如果他们覆盖了一个基类函数,他们必须标记为override的。如果没有这么做则会得到一个编译时错误。类似的,如果一个标记为override的函数没有覆盖基类的相应函数,同样得到一个编译错误。这一行为将的类更加的清晰和降低运行时崩溃的可能性。
&翻译的不错哦!
4.&多返回值
在我加入Atomic之前的那份工作中,我主要开发C++,并且经常发现自己写一些输出多值的方法。我总是为决定使用引用,pair还是将返回值包装为struct或class来作为参数传递而挣扎不已。
在Swift中,有内置的机制来用一个由任意数量的带标记的数据成员的组成的元组作为多返回值。
46&&func&calculateStatisticsFromArray(data:&Double[])&-&&(mean:&Double,&median:&Double,&mode:&Double)&{
47.&&&&&&&&&&return&(62,&44,&12)
49&&var&stats&=&calculateStatisticsFromArray([])
stats:&(mean:&Double,&median:&Double,&mode:&Double)&=&{
&&mean&=&62
&&median&=&44
&&mode&=&12
50&&stats.mean
&&$R10:&Double&=&62
51&&stats.median
&&$R11:&Double&=&44
52&&stats.mode
&&$R12:&Double&=&12
Swift同样支持声明多个变量来显示的获取多返回值:
&51&&var(mean,&median,&mode)&=&calculateStatisticsFromArray(data)
&翻译的不错哦!
5. willSet, didSet
在 Swift中,&类具有属性(类似于Objective-C)。有一个声明setter和getter的机制,如果属性的类型是简单的并且需要计算的,但是你想观察他们的变化,两个内置的需要覆盖的方法-willSet和didSet将会有帮助。
1&&class&Container&{
2.&&&&&var&someValue:&Double&=&0.0&{
3.&&&&&&&&&willSet&{
4.&&&&&&&&&&&&&println("willSet!&\(newValue)")&
5.&&&&&&&&&}
6.&&&&&&&&&didSet&{
7.&&&&&&&&&&&&&println("didSet!&\(someValue)")
8.&&&&&&&&&}
11&&var&c&=&Container()
&&c:&Container&=&{
&&&&someValue&=&0
12&&c.someValue&=&100.0
&&willSet!&100.0
&&didSet!&100.0
&翻译的不错哦!
REPL表示“read-evaluate-print-loop”,我认为它是通过沙箱测试输出的代码.您可以输入代码到REPL,如果您在使用一个IDE或者喜爱的文本编辑器,它会被编译/解释和实时运行,并马上能让你看到输出结果。这是开发人员努力学习这门新语言时能看到的一个强大功能。我一直在使用它来学习swift。
使用SWIFT REPL:
1.下载Xcode 6 Beta版。
2.在终端中运行以下命令来切换路径xcrun:
&sudo xcode-select -switch /Applications/Xcode6-Beta.app/Contents/Developer. &
&注意:如果要重置,请在使用终端里键入 xcode-select -r
3.xcrun swift
&翻译的不错哦!
谁将拥有未来?
我坚信,在iOS 8和以后的版本中,Swift将成长成为iOS开发的主流语言。尽管如此,苹果已经向我们保证,在运行时将保持二者兼容:我们的Objective-C应用程序仍然可以继续工作。同样清楚的是Swift的语法和语义将改变后的iOS 8。苹果告诉我们,他们不能保证源代码的兼容性,但会提供代码转换器。使用代码转换器的想法让我害怕了一下,我认为迁移到Swift的时间会来得早(iOS的8-9的样子),而不是以后。
&翻译的不错哦!从原理分析Swift的switch怎么比较对象 - 简书
<div class="fixed-btn note-fixed-download" data-toggle="popover" data-placement="left" data-html="true" data-trigger="hover" data-content=''>
写了223791字,被5817人关注,获得了4552个喜欢
从原理分析Swift的switch怎么比较对象
今天突然想到一个问题,让我觉得有必要总结一下switch语句。我们知道swift中的switch,远比C语言只能比较整数强大得多,但问题来了,哪些类型可以放到switch中比较呢,对象可以比较么?
对switch的用法给出了这样的解释:
Cases can match many different patterns, including interval matches, tuples, and casts to a specific type.
也就是说除了最常用的比较整数、字符串等等之外,switch还可以用来匹配范围、元组,转化成某个特定类型等等。但文档里这个including用的实在是无语,因为它没有指明所有可以放在switch中比较的类型,文章开头提出的问题依然没有答案。
我们不妨动手试一下,用switch匹配对象:
var o1 = A()
var o2 = A()
switch o {
print("it is o1")
print("it is o2")
print("not o1 or o2")
果然,编译器报错了:“Expression pattern of type 'A' cannot match values of type 'A'”。至少我们目前还不明白“expression pattern”是什么,怎么类型A就不能匹配类型A了。
我们做一下改动,在case语句后面加上let:
switch o {
case let o1:
print("it is o1")
case let o2:
print("it is o2")
print("not o1 or o2")
OK,编译运行,结果是:it is o1。这是因为case let不是匹配值,而是值绑定,也就是把o的值赋给临时变量o1,这在o是可选类型时很有用,类似于if let那样的隐式解析可选类型。没有打出it is o2是因为swift中的switch,只匹配第一个相符的case,然后就结束了,即使不写break也不会跳到后面的case。
扯远了,回到话题上来,既然添加let不行,我们得想别的办法。这时候不妨考虑一下switch语句是怎么实现的。据我个人猜测,估计类似于用了好多个if判断有没有匹配的case,那既然如此,我们给类型A重载一下==运算符试试:
class A {}
func == (lhs: A, rhs: A) -& Bool { return true }
var o = A(); var o1 = A() ;var o2 = A()
switch o {
print("it is o1")
print("it is o2")
print("not o1 or o2")
很显然,又失败了。如果这就能搞定问题,那这篇文章也太水了。报错信息和之前一样。可问题是我们已经重载了==运算符,为什么A类型还是不能饿匹配A类型呢,难道switch不用判断两个变量是否相等么。
switch作为一个多条件匹配的语句,自然是要判断变量是否相等的,不过它不是通过==运算符判断,而是通过~=运算符。再来看一段的解释:
An expression pattern represents the value of an expression. Expression patterns appear only in switch statement case labels.
以及这句话:
The expression represented by the expression pattern is compared with the value of an input expression using the Swift standard library ~= operator.
第一句解释了之前的报错,所谓的“express pattern”是指表达式的值,这个概念只在switch的case标签中有。所以之前的报错信息是说:“o1这个表达式的值(还是o1)与传入的参数o都是类型A的,但它们无法匹配”。至于为什么不能匹配,答案在第二句话中,因为o1和o的匹配是通过调用标准库中的~=运算符完成的。
所以,只要把重载==换成重载~=就可以了。改动一个字符,别的都不用改,然后程序就可以运行了。Swift默认在~=运算符中调用==运算符,这也就是为什么我们感觉不到匹配整数类型需要什么额外处理。但对于自定义类型来说,不重载~=运算符,就算你重载了==也是没用的。
除此以外,还有一种解决方法,那就是让A类型实现Equatable协议。这样就不需要重载~=运算符了。答案就在Swift的module的最后几行:
@warn_unused_result
public func ~=&T : Equatable&(a: T, b: T) -& Bool
Swift已经为所有实现了Equatable协议的类重载了~=运算符。虽然实现Equatable协议只要求重载==运算符,但如果你不显式的注明遵守了Equatable协议,swift是无法知道的。因此,如果你重载了==运算符,就顺手标注一下实现了Equatable协议吧,这样还有很多好处,比如SequenceType的split方法等。
最后总结一句:
能放在switch语句中的类型必须重载~=运算符,或者实现Equatable协议。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
分享 iOS 开发的知识,解决大家遇到的问题,讨论iOS开发的前沿,欢迎大家投稿~
· 28014人关注
iOS,Swift,OC,架构
· 3998人关注
纪录开发的点点滴滴,相互学习,共同进步~
专题内容主要包括Object-C、Swift等开发技巧以及学习过程的内容
· 2152人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:Swift 3 新特性一览
自 Swift 开源以来,Swift 的开发与演变已经完全由社区和核心团队所共同主导,它们在 Swift 3 当中为我们带来了巨大的变化和改进,而这正是我们所需要详细了解的。这些变化的原因与 Swift 邮件列表当中那些冗长、深入的讨论密切相关,都是经过充分地论证才执行这些变化的。然而,我们中的大部分人其实并没有很多时间去尽可能地跟上这些变化,不过在本次 App Builders CH 的讲演中,Daniel Steinberg 会带您快速了解那些「关于 Swift 3 您不得不知道的那些事情」。所涉及的范围包括了新的关键词、移除 C 风格的 for-loop 循环从而遵循新的 API 设计指南,此外还有其他常见的争议话题,Daniel 将会快速带您了解过去一年来,Swift 都发生了哪些改变。
编者按:要查看 Swift Evolution 远程仓库中官方核心团队是如何对 Swift 提案进行考虑的,您可以点击每个章节开头所对应的提案编号。
Swift 3 有哪些新特性?
Swift 现已开源。
很多其他社区的人都会问这样一个问题: 『这是否真的意味着 Swift 开源了?』
是的,没错,Swift 现已开源。
我们当中有很多人已经很熟悉开源语言了,比如说开源的 Java、开源的其他语言等等,这些人对贡献开源语言已经无比熟悉。这是非常惊人的一点。Swift 的邮件列表是 非常 开放的。关于这门语言的讨论是公开进行的。核心团队会谈论他们的想法,以及为什么他们要这么做。他们还会从社区中采纳合理的建议,并且允许所有人都参与到这个过程当中来。
这样一来,我们就已经能够知道 Swift 3 将会出现哪些新特性了。
0025 – 限定作用域的访问级别
所以,如果您想问 Swift 的未来会是怎样的话,那么我想说在即将到来的 Swift 3 当中,有一个很重要的东西将会出现,那就是不同的访问级别 (access level)。所以在这一点上,我们有 3 种访问级别:
公开(public)
内部(internal)
私有(private)
默认的访问级别是 internal ,这意味着此成员只能在模组 (module) 内可见。如果要让其能够被模组外的成员访问到的话,那么就要将其设置为 public 。此外就是 private 了,在 Swift 中这和其他语言的「私有」访问级别是有所不同的,这意味着私有成员能够在文件内可见。
在 Swift 3 当中,我们将迎来另一种访问级别, private 将会被重命名为 fileprivate ,这就让这个访问级别变得十分清晰:文件私有成员只能够在文件内可见;我们将得到的第四种访问级别就是 private ,这也就是说即使是在同一个文件当中,私有成员也只能够在对应的作用域当中访问。
举个例子,在这个文件当中我创建了一个类。在这个类当中我添加了一个 private 的属性和一个 private 的方法。这两个成员只能够在这个类当中可见,就算位于相同文件内,其他类也是无法访问这两个成员的。因此,也就是说,在过去,这两个成员在整个文件当中都可以访问得到;而到了现在, private 方法只能够在相同类当中可见了。也就是说,昔日的 private 变成了今日的 fileprivate ,而新的访问级别让我们有了更私有的访问权限。
有很多人很希望能够看到诸如 protected 之类的东西,这样子类就可以访问到父类的属性。我们并不打算引入这个东西,至少对于 Swift 3 来说。因此,Swift 3 中最终的访问级别就是:
公开(public)
内部(internal)
文件外私有(fileprivate)
私有(private)
0004 – 移除 ++ 和 --
另一个变化就是 ++ 和 — 运算符被移除了,这可能会让很多人非常伤心。当然,您也能够例举出很多您为什么需要它们的理由。
不过在绝大多数情况下您是不会使用这两个运算符的,不过一旦您需要让某个量自增的时候您往往就会想使用它们了,不过我们不再能够使用 count++ 了。
while count & upperLimit {
print(myArray[count++])}
现在,我们如果要让某个量自增的话,我们就必须要使用明确的 count += 1 了。
while count & upperLimit {
print(myArray[count])
count += 1}
通常情况下,如果您需要让某个量自增,并以此来获取返回值的话,那么接下来您还必须将增加的量减回去,因此有些时候代码写起来就不如从前一样好看了。
0007 – 移除带有条件和自增的 for-loops C 风格循环
for (int i = 0; i & array. i++)
由于 ++ 被移除了,那么 C 风格的 for-loops 循环被移除只是时间问题了。因此,C 风格的 for-loops 循环将不能在 Swift 3 当中可用;事实上,如果您正在使用 Xcode 7.3 ,那么您会注意到很多警告,提示一旦 Swift 3 发布这些特性都将失效。
0053 – 从函数参数中移除 let 的显式使用
func double(~~let~~ input: Int) -& Int {
这是一个很有意思的特性。我不知道各位是否有深入思考过这种特性的意义,不过当您在 Swift 当中调用一个方法的时候,方法将会将您所传递的参数拷贝出一个不可修改的副本。这里真正所暗示的是 let 这个单词,虽然没有人会主动写下这个这个词,因为 let 是默认的行为。这个 let 已经不复存在了。
这是 Swift 当中最近的一个变化。之所以会推出这个变化是因为下一个提案的出现:
0003 – 从函数参数中移除 var
这个提案将 var 从函数参数当中移除掉了,他们说:一旦我们移除了 var ,那么我们是不是也要把 let 移除掉?我们中的很多人都一脸懵逼,什么?之前还可以在这里写 let ?
func double(~~var~~ input: Int) -& Int {
input = input * 2
return input}
举个例子,在这个方法中我获取了 input 这个参数,然后我想要让其翻倍然后作为返回值返回,要记住我是对一个不可修改的副本进行操作的,因此我是没办法修改 input 的值的。因此,如果我们不想再声明一个新的变量的话,我们之前会选择再声明中使用 var ,以让其变成一个可修改的副本。
但是这仍然是一个副本,不过它现在可以修改了,因此我就可以修改它的值。这段代码目前是没法用了。 var 已经被移除掉了,我们必须要在这里显式声明一个新的变量。
func double(input: Int) -& Int {
var localInput = input
localInput = localInput * 2
return localInput}
在这里,我创建了一个名为 localInput 的可修改副本。我使用 input 对其进行赋值,这样我就可以对可修改副本进行操作了。不过绝大多数人可能会选择这样子做:
func double(input: Int) -& Int {
var input = input
input = input * 2
return input}
他们使用相同的名称来创建变量,这就是一种被称为__命名遮罩 (name shadowing)__ 的特性,而不是使用其他的名称来为局部变量命名。这会让一些人感到困惑;当然也有许多人喜欢这样做。之所以会迷惑人的原因在于:两个输入当中的值截然不同。右边的那个 input 值是参数值,而左边的 input 则是能够被改变的值。
在这个例子当中,可能并不太让人困惑。我觉得为了让语法更为清晰,我们应该使用 var input 的方式,这样就不必将参数值赋值回 input 当中了,这样我们就可以以之前使用可变参数的方式来使用这个变量了。
0031 – 将 inout 声明调整为类型修饰
虽然我说过参数是一种不可修改的拷贝,因此如果您想要获取一份可修改的拷贝的话,您需要在下面的代码中使用 var 来单独创建一个局部变量。不过如果您切实想要修改您传入的参数值,并禁止拷贝的发生,那么至今为止只能够使用 inout 的方式。
func double(input: inout Int) {
input = input * 2}
inout 参数现在不出现在参数名的前面了,不过它也没有跑得太远。现在它成为了类型的一个修饰符,而不是变量名称的一个部分。因此,只需要将其向右边调整一点点就可以了。
之所以做这样的决定,是因为实际上 inout 确实只是一个额外的描述符而已,它并不是参数名的一部分,因此需要把它移到正确的地方。
0035 – inout 限制为只能捕获 @noescape 上下文
关于 inout 发生的另一个改变,在于 inout 的捕获机制目前受到了限制。
func escape(f: () -& ()) {}func example(x: inout Int) {
escape { _ = x }}
假设我有一个名为 escape() 的函数,它接受一个简单的方法作为其参数。在 example() 方法当中,我引入了 inout 类型的 x 参数,我会在函数当中使用这个参数,也就是将其传递到 escape() 当中。
因此, escape() 现在就会对 inout x 开始进行操作,而这个时候会造成一个问题,由于 inout 的开销非常大。 inout 换句话说会对我传入的变量进行修改,二这个时候我并不知道 example() 是否能够继续执行,调用我在 example() 本身作用域范围之外的函数。
为了解决这个问题,我们必须要说明『嗯,我只在这里面使用它;不要担心编译器会出错!』,因此我们可以在这里使用 @noescape 进行标记:
// safe because @noescape =& closure// can't be called after function returnsfunc noEscape(@noescape f: () -& ()) {}func example(x: inout Int) {
noEscape { _ = x }}
如果这个方法参数被标记为 @noescape ,那么它就可以正常使用了。编译器知道我传递的这个函数不会使用任何作用域范围之外的东西,因此程序就能够正常执行。
我们还有第二种方法来处理。
// [x] is a capture list// a constant is initialized to have the value of xfunc escape(f: () -& ()) {}func example(x: inout Int) {
escape { [x] in _ = x }}
如果 example() 在 escape() 当中使用了这个 inout x ,这个时候它不是一个数组,虽然它看起来像。现在这玩意儿叫__捕获列表 (capture list)__。当您需要在捕获列表当中将某个量标记为 weak 或者 unowned 的时候,就需要使用这种形式。这里我们只是明确的说:我想要对 x 进行捕获,默认情况下的捕获级别是 strong 类型的。这同样也表达了『没错,我是在使用这个 inout x ,但是我准备在这里捕获它的现有值。』这样做就会对传递给 inout 的变量创建一份拷贝,这样就不用担心会发生问题了。
因此,如果要处理闭包当中的 inout 值的话,我们可以采用这两种方法进行。
0049 – 将 @noescape 和 @autoclosure 转变为类型特性
好的,之前关于 @noescape 的内容我发现我写错了, @noescape 的位置发生了改变,它现在不能放在那里了。
func noEscape(f: @noescape () -& ()) {}func noEscape(f: @autoclosure () -& ()) {}
Swift 3 即将发生的变化之一,就是将这些参数用以描述被传递的实际函数,而不是放在外面,这对 @autoclosure 也是同样的道理。如果您发现您的代码在迁移之前完全不能运行了,这是怎样的一种体验?
0002 – 移除柯里化函数声明语法
「移除柯里化 (curried) 函数声明语法」可能会让很多人感到焦虑,其实完全不必,因为他们误解了这句话的意思,并不是说 Swift 移除了柯里化特性。他们并没有移除柯里化。他们只是将柯里化函数的一种写法移除掉了。
func curried(x: Int)(y: Int) -& Int {
return {(y: Int) -& Int in
return x * y
举个例子,在这个柯里化函数当中,注意到它接受 X 和 Y,然后返回 Int 。如果看得仔细一点,您会发现它其实是先接受一个参数,然后再接受另一个参数,因此需要这样子调用: curried(7)(8) 。这很容易让人误解。不是说调用的部分,而是说定义的部分很容易让人误解。这样定义的方式将被移除,因为这里实际发生的只是对 7 进行柯里化而已。
func curried(x: Int) -& (y: Int) -& Int {
return {(y: Int) -& Int in
return x * y
我向 X 传递 7 这个值,然后我得到一个返回的函数值,这个函数就是将您传递进来的值乘以 y。随后当我传递 8,得到的结果就是 7 x 8.
因此我实际上是将它分成了两部分。我使用了一个柯里化函数来捕获 X。这实际上就是闭包;一旦捕获成功,我再将 Y 传递进去,然后就执行后续的操作。
Swift 核心团队是这么说的:「看吧,这种做法很容易让人迷惑,因为这里出现了一堆堆的括号,让我们更明确一点,要说明您正在做的是什么。比如说您传递了 X 之后,它会返回一个函数,然后再讲这个函数应用到下一个元素当中」。因此,柯里化仍然存在,这是语法发生了变化。
0022 – 将 Objective-C 的 selector 变为方法的引用
我想要谈及一些发生在 Objective-C 上的变化。其中之一就是我们需要改变使用 Objective-C selector 的方式,它现在变成了方法的引用。随着 Swift 3 的退出,我们需要使用 #selector 来实现这个功能。对于那些从 Objective-C 转过来的开发者来说,这一点看起来非常熟悉。这个用法就是和 @selector 类似,和诸位此前所做的是很类似的。
#selector(callbackMethod)#selector(MyClass.callbackMethod)#selector(MyClass.callbackMethod(with:))
您会注意到在 @selector 当中,您需要在调用这个 callback 方法的时候加上冒号,因为这个方法需要接受参数。在这个例子当中,由于这里只有一个 callback 方法,因此我们就可以在这里使用这样的形式。如果我们需要调用其他类的方法,我们可以在 selector 当中使用点语法调用该类的方法。不过如果我们又添加了一个同名但是参数不同的方法的时候,就会发生错误。
我们必须要指明我们需要调用的是哪个方法,要做的就是要明确指明需要调用的是哪个类,并且指明要接受的参数标签是什么。不过如果您不需要指定类或者参数的时候,您可以将它们忽略掉。
0033 – 将 Objective-C 常量变为 Swift 类型
我很喜欢另一个与 Objective-C 相关的 Swift 的特性。开发团队这段时间以来都在尽量让大家习惯这样的用法,大家可以好好回想一下。比如说那些 TableCellRowAnimations、所有的 UIButton 类型、所有的 UIButton 状态等等。如果您之前使用了这种类型的常量的话,那么开发团队会尽量让您切换为使用相同前缀的常量用法:
HK_EXTERN NSString * const HKQuantityTypeIdentifierBodyMassIHK_EXTERN NSString * const HKQuantityTypeIdentifierBodyFatPHK_EXTERN NSString * const HKQuantityTypeIdentifierHHK_EXTERN NSString * const HKQuantityTypeIdentifierBodyMHK_EXTERN NSString * const HKQuantityTypeIdentifierLeanBodyM
现在他们会说「如果您听从了我们的建议,并且按照我们所说的做了,那么我们就可以将其翻译到 Swift 当中来,这些东西会变成枚举」:
enum HKQuantityTypeIdentifier : String {
case BodyMassIndex
case BodyFatPercentage
case Height
case BodyMass
case LeanBodyMass}
这样您的 API 就能很好地融入到 Swift 当中,并且这种用法也和官方 API 类似,而这正是未来您所能看到的变化之一。
那么我们是怎么知道它是字符串的呢?您只需要看一下您引入的这些常量就会发现,它们全都是字符串。因此我们就知道这些枚举的元数据必须是字符串。因此这就是我们最终所能得到的枚举。真的非常好用。
0005 – 更好地将 Objective-C API 迁移到 Swift
通常情况下,我们在使用 Objective-C API 的时候,它们已经能很好地翻译为 Swift 了。有一个例子是将变量方法明精简,并去除掉某些重复的内容。
// Swift 2.1let color = NSColor.blueColor()// Swift 3.0 - prune redundant type nameslet color = NSColor.blue()
如果有这样一个蓝色的 NSColor,由于 NSColor 和 blueShadow 有重读的地方,因此这里就需要被隐藏掉,这样它就会简单地蒙蔽住了。
下面的例子展示了一些更有用、更自然的例子:
// Swift 2.1rootViewController.presentViewController(alert,
animated: true,
completion: nil)// Swift 3.0// Nullable trailing closures default = nil// Animated default = true// Prune redundant type namesrootViewController.present(alert)
您会发现 Swift 的词语更加精简了,并且我们还并没有遗漏任何信息。它不再啰嗦,这样您就不必一遍又一遍地重复相同的词语了。
接下来,如果我有一个布尔属性的话,接下来在 Swift 3 当中发生的改变就是会在这些属性前面预加上 “is”,因此所有我们从 Objective-C 获取的类似 getter=isSomething 之类的属性的时候,转换成 Swift 就会变成 isEmpty 之类的样式。
// Swift 2.1addLineToPoint(myPoint) // Callingfunc addLineToPoint(_: CGPoint) // Definition// Swift 3.0 - prune redundant type namesaddLine(to: myPoint) // Callingfunc addLine(to point: CGPoint) // Definition
再次强调一遍,减少冗余;这里我调用了一个将 line 添加到 point 上的方法,然后我将 point 传递进去,所以这个方法写出来是这个样子的。那么如果我们必须要传递 point 的时候就必须要声明 “我要将 point 传递到 point “当中呢?因此,在 Swift 3 中您将会看到大量的类似例子。这一类的示例将到处都是。在 Swift 3 中, 外部标签 变成了 to, 内部标签 变成了 point,这样当我们在调用的时候,就可以很简单的说:将线添加「到」「某个点」上了。
我没有必要告诉其他人这是一个 point。只要你知编译器知,那么久万事大吉。我发现现在的这种使用方式越来越像是在和编译器对话了。
func addLine(to point: CGPoint) // Definition
注意到在最后面这个例子当中,我们添加了首参数标签。我们在 Swift 当中将会大量使用它,因此我们需要讨论得更深入一点。通常情况下,外部标签是显示给调用者看的,我们并不会使用它,而内部标签则是给我们自己看的,因此需要使用。
另一个让有些人很沮丧的是:如果 URLHandler 是作为属性进行声明的,那么它的前面将会被改写成小写样式。
var URLHandler// turns intovar urlHandler
另一个让我很不开心的就是,这种规则同样也出现在枚举当中了。
enum Currency {
case Dollars
case Euros
case Pounds
我们不能够再写出这样的枚举了,我们必须要向下面这样写枚举,这看起来怪怪的。
enum Currency {
case dollars
case euros
case pounds
但是习惯就好,四个月后我们就会觉得这才是枚举应有的样子。
0036 – 在实现枚举实例成员的时候,需要加上左前缀点
enum Currency {
case dollars
case euros
case pounds
var symbol: String {
switch self {
case .dollars:
return &$&
return &I don't know&
在枚举当中,我们需要使用左前缀点 (Leading Dot prefixes),这意味着:现在这是位于枚举内部,对 self 进行 switch,以便找到 dollars 这个 case。目前这里有两种写法,自 Swift 3 开始,左前缀点就必需加上,即使是在枚举内部进行使用。也就是说,左前缀点在枚举外部是始终都需要的,而自 Swift 3 开始,每个 case 前面都要加上左前缀点。我很喜欢这一类的变化,因为它能确保代码的一致性。这意味着我们能够明确知道每个词的用途,也能够知道将会发生是呢嘛操作。这意味着不管什么样的代码出现,我们都能意识到这是一个枚举。这难道不是一件很赞的事情么?
0043 – 可以在 case 标签后声明多个关联值变量
enum MyEnum {
case case1(Int, Float)
case case2(Float, Int)}switch value {case let .case(x, 2), let .case2(2, x):
print(x)case .case1, .case2:
在我的这个枚举当中,我写了两个 case,注意到 case1 中 Int 是放在前面的,而 case2 中 Int 是放在后面的。因此,当我需要对这个值进行 switch 的时候,我需要分别指明 case1 的第二个关联值是 Float 类型,case2 的第一个关联值是 Float 类型,并且剩余的部分我将值赋为 2.
自 Swift 3 开始,我可以将这些操作放到一步当中来。我们不再使用分离的 case 语句来编写,也就是说我们不必多次撰写相同的 case 声明语句,我可以在一行当中将这两个 Float 值直接关联到,我们使用 x 来表示这个 Float 值。这样我们就可以直接使用 x 来进行相关的操作了。这样做的好处是,如果枚举 case 不满足其中的某一个,那么它就会选用另一个进行匹配,直到两者都不满足匹配为止。
最后面那个写法是有点愚蠢,不过我还是写出来,这样可以让大家更明显地看到它们的区别。
0001 – 允许(绝大多数)关键词作为参数标签
// Swift 3 calling with argument label:calculateRevenue(for sales: numberOfCopies,
in .dollars)// Swift 3 declaring with argument label:calculateRevenue(for sales: Int,
in currency: Currency)
允许绝大多数作为参数标签存在了。比如说 var 即将成为参数标签。但是有一点很奇怪:当我们迁移到 Swift 3 的时候,您会发现介词将会被大量使用,比如说 calculateRevenue 这个方法当中的 for 和 in 。现在这些关键词可以用在参数标签上面了。
它们实际上可以让您的代码更具备可读性,从上面的代码中就可以看出来了。我们现在可以在语言级别使用绝大多数关键词作为参数标签了。
0009 – 访问实例成员需要 self
在 Objective-C 当中,我们到处都在使用 self。如果您之前是写 Java、C# 的话,那么您很可能只会在必需的时候才使用 self。在 Swift 之后的版本中,我们只需要在必要的时候才需使用 self。
struct Friend {
let name: String
let location: String
func nameBadge() {
print(&I'm&, self.name, &from&, self.location)
// REJECTED: self. not required
在这个例子当中,我们有两个属性:name 和 location,此外还添加了一个 nameBadge 方法,这个方法中使用了这两个属性。对大家来说,这里的示例是非常清楚的,我们很清楚它们引用的是属性而不是其他东西。这可能会让有些人感到失望。他们总喜欢到处使用 self,就像我们此前在 Objective-C 当中所做的那样,现在我很高兴地告诉大家这种做法将会被拒绝。
0011 – 将用于关联类型声明的 typealias 关键字替换为 associatedtype
类型别名 (Type alias) 是一个挺有意思的玩意儿。在 Swift 中类型别名有两种不同的用途:
protocol Prot {
associatedtype Container : SequenceType}extension Prot {
typealias Element = Container.Generator.Element}
它的作用是:「这是一个占位符。您需要明确告知 Container 随后会关联哪种类型,另一种的用法和 #define 很类似,将 Element 作为 Container.Generator.Element 的替代。」Swift 目前这样设计的目的在于要将这两种用途给分离开来,在第一种情况下,它只是一个占位符。随后您需要明确告知它所代表的类型。我们将它从 typealias 更改为 associatedtype 。
再次声明一点,这个设计是非常好的,因为之前我们必须要使用相同的关键词来完成两种极为不同的用法,而现在两种用法都有各自的关键词归属了。
0046 – 将所有参数标签进行一致化,包括首标签
我们将为所有参数标签进行一致化操作。不知道诸位还记得在 Swift 1 当中,您需要写出函数中的所有参数标签,其中也包括首标签。这个时候您可能会选择将函数放到某个类或者某个方法当中,以确保不写那个讨厌的首标签。在 Swift 2 当中,开发团队将首标签给抛弃掉了,以便统一两者的行为,但是在构造器中仍是不一样的。自 Swift 3 开始,我们将必须写出所有的参数标签。
// Swift 3.0func increase(ourNumber: Int, delta: Int) -& Int {}increase(ourNumber: 6, delta: 3)
比如说这个例子当中,当我们调用这个方法的时候, delta 在 Swift 2 当中是必须要写出来的,但是 ourNumber 是不会显现的。自 Swift 3 开始, outNumber 作为首标签就必须要写出来了。所有的参数标签都必须这么做。
「但是 Daniel」,您可能会问了,「有些时候我不想让这个首标签显示出来。」好吧,您仍然可以使用下划线来将其隐藏掉。
// Swift 3.0func increase(_ ourNumber: Int, delta: Int) -& Int {}increase(6, delta: 3)
0023 – Swift API 设计指南
或许,Swift 演变清单中最热门的讨论就是关于 Swift 未来的 API 设计指南了。如果您还没有看过这个清单的话,那么我建议您之后好好地阅读一下上面的这些提案,这样您就能够对未来有所预见。这上面的内容非常繁杂;您只需要挑选感兴趣的那部分查看就行了。
我们会在这里讨论关于 Swift API 指南的相关内容,以及我们该如何命名相关的元素。您会发现这个话题非常的火爆,如果您不想查看这些讨论的话,您还可以去 Swift.org 上去观看正式的 Swift API 指南。
// Name functions and methods according to their side-effects// Array:sort()sorted()
在 Swift 3 中,我们将转回到 Swift 1 的命名约定来,因为它已经足够优秀了。我们要根据函数和方法的侵染效应 (side effect) 来决定它们的命名方式,关于这方面有许多的例子。
第一个例子是:如果方法没有任何的侵染效应(也就是不会对变量本身做出修改),那么它应该以名词的方式进行命名。例如: distance() 或者 successor() 是没有任何侵染效应的。
x.distance(to: y)i.successor()
如果存在侵染效应的话,那么就应该以祈使短语的方式进行命名。如果我需要排序的话,我们应该要说:某某,对自己排序;或者 X 添加 Y 元素。
x.sort()x.append(y)
现在,将这两个规则结合起来,我们有:如果我有一个可修改版本,它对应了一个不可修改的版本,那么它应该使用 “-ed” 或者 “-ing” 后缀,因为要 完全明确其涵义 。不加后缀的版本我们第一反映会认为其是『原生的』方法。每次看到这种类型,我都必须要看一下哪个方法满足我的需求。
sort()// vs.sorted()/sorting()
当然,您也可以总是按住 Option (?) 键然后点击这个方法,然后查看它有没有返回值,以便确定它是否是可修改的类型。当然,关于命名这一点还有很多建议。这些建议可能会发生变化,不过这是目前 Swift 3 所采取的方式。
在 Swift 2 当中,官方鼓励我们尽可能使用方法而不是使用全局函数。在 Swift 1 当中,您或许还记得那些诸如 map 、 filter 、 reduce 之类的全局函数。这些都属于全局函数。您必须要将数组传递进去,然后标明您如何对其进行变化。之后我们有了协议扩展,这些函数就变成了 SequenceType 中的方法,这样 map 、 filter 、 reduce 就定义在了 SequenceType 协议当中了。
通常而言,您都应当尽量使用方法,而不是使用全局函数,这正是我们所推荐的。不过仍然有一些例外,例子如下:
min(x, y, z)
如果没有明显的对象能够单独调用这个 min 方法的话,那么使用全局函数仍然是可以的。和 array.filter 有所不同,您所调用的数组是 filter 的主体对象,而如果要取三个数字的最小值的话,就没有任何主体了。
print(x)sin(x)
如果函数是不受约束的泛型函数的话,那么将其作为全局函数也是可行的,比如 print 之类。我们可以使用 Math.sin() ,但是我们通常情况下都会使用 sin() 。我们为什么要改变它呢?因此,除非它本身就推荐使用域符号,否则的话我们就按照习惯来就可以了。
方法可以共享一个基础名称。要注意到我们在 Swift 中可以使用重载 (overload) 机制,而这正是 Objective-C 当中所没有的。因此,当某个方法有三种不同版本的时候,使用这个机制可以完成一些类似的任务。共享基础名称是一个非常好用的功能。
然而,如果您看一下 UITableView 的 API,您会发现几乎每个方法都被命名为 tableView! ,然后在内部再执行不同的操作。我希望您在执行相似任务的时候,将这类方法都以一个基础名称进行命名。
// Choose good parameter namesfunc move(from startingIndex: Int, to endingIndex: Int)
这一点似乎我们没必要和大家说明,但是我们还是想提及这一点,因此如果您有一个 move() 函数的话,那么添加起始索引以及结束索引是一个非常好的选择,因为这样就能很清晰地看到所引用的类型。我觉得这个特性是专门为那些从 Java 迁移过来的人所准备的……
// Take advantage of default valuesfunc hello(name: String = &World&)// Prefer to locate parameters with defaults at the endinit(name: String, hometown: String? = nil)
另一个很方便的东西就是要利用好默认值,这样就不必写一堆 hello() 的不同版本。
默认值所带来的好处是您可以在构造器当中摆脱大量无用值的编写。如果您有一大堆东西需要构造,如果其中包含有可空值,并且可以被设置为空的话,那么它们的默认值就应当被设置为空。这样一来,您不仅可以设置有 hometown 数值的变量,还可以通过少写一个参数来快速设置一个 hometown 默认为空的变量。
我发现这极大地精简了代码。这样就可以避免了大量重复的构造器参数声明。
通常情况下,虽然这不是一个必须的要求,不过如果您使用了默认参数,那么最好将它们置于函数的末尾。因为它们不必成为命名参数 (named parameters)。因此如果您在其他语言当中拥有多个默认值,一旦您指明了某个默认值参数,那么您就必须要指明其之后的所有默认值参数。我们没必要在 Swift 当中这么做。这并不是一个必须的要求,但是这会让代码变得更为干净整洁。
// Definitionfunc move(from startingIndex: Int, to endingIndex: Int)// Callingmove(from: here to: there)
我们已经讨论了内部标签,但是您也应当让外部参数标签干净整洁,从而让使用该方法的人们能够在调用的时候就能够轻易理解这个方法,而不是让它要到函数定义当中去研究如何使用。
什么是一个好的 API 呢?对我来说就是「从」这里「移动」「到」那里。即使第一个内部名称是「起始索引」,第二个内部名称是「结尾索引」。在方法的内部我会使用这两个名称。
考虑到您的 API 总会发布给别人去使用,我发现在 Swift 3 中似乎介词更受欢迎一些。这是所有人都热切期盼的。
对于参数标签来说,还是存有一些例外的。例如,让我们回到 min 那里去:
// Omit labels when arguments can't be distinguishedmin(number1, number2)
min 当中的变量位置对结果没有任何影响。我并不关心第一个数字是哪个,第二个数字是哪个。因此,我不必添加任何的参数标签来区分它们。如果参数之间并没有任何的区别的话,为什么要给它们添加标签呢?这完全没问题。
// Omit labels in full width initsDouble(someInt)Double(_ anInt: someInt)
如果您想要创建一个从 Int 转换到 Double 的方法,我有一个建议,那就是没必要声明参数标签。事实上,如果您看一下标准库当中是如何定义的,您会发现他们将第一个参数标为了下划线。如果您执行这些构造器的话,您完全没必要声明外部参数。因此您可以看到,我们可以直接简单的调用 init,而不是使用 Objective-C 风格的 initWithSomethings 。
有些时候您会看到调用的 init 构造器的第一个标签是 frame,但是如果参数是数值类型,并且作用是将其转换为另一个数值类型,那么我们就不必指明外部参数。
// When the preposition applies to the each parameterfunc move(from startingIndex: Int, to endingIndex: Int)// When the preposition applies to the wholefunc moveTo(x: Int, y: Int)x.removeBoxes(having Length: 12)
介词:在这种情况下,我们往往使用 from…to 介词来表明动作从某个地方执行到另一个地方,但是有些时候动作的范围可能同时适用于这两个地方,这个时候我们就倾向于将介词移动到外面来。通过将介词移到外面来,然后将那两个标签保持在内部,在这个例子当中,由于里面的参数只有一个,因此 boxes 在外面还是在里面都是没有任何影响的。
好的,我的演讲到此结束!这个讲演只是一个关于 Swift 3 演变和改进的快速概览。谢谢大家!
问:有时候我觉得省略 self 会让其他开发者阅读我代码的时候觉得难以理解。您对明确声明 self.property 以及直接声明 property 怎么看呢?
Daniel:我的答案和 Apple 的稍有不同。Apple 的答案是,你想使用 self 就用,想不用就不用,选择权在你手上。因此,您的团队可以为了阅读性显式声明 self ,而其他团队则选择省略。
就我个人而言,我认为当方法中有一个局部变量的名称和属性的名称相同的时候,这种省略的做法就会变得非常混乱。在这种情况下,我会使用 self.property 来明确两者之间的区别。不然的话我就会简单地使用 property ,因为它是相对明确的,就是直接来自实例变量的,对不对?一部分是处于简洁起见,另一部分则是个人偏好了。
问:您对隐式解包可空值有什么看法呢?何时该使用,而何时不该使用?
Daniel:UIKit 在某些时候需要使用隐式解包,例如 IBOutlet 之类的,因为实例化这些输出口是没有价值的;它们会在随后故事板加载的时候进行实例化,因此它们在您代码当中基本等同于永恒存在。这个时候就是使用隐式解包的绝佳良机,因为这就非常必要。我很喜欢看到这种使用输出口的延迟加载方式,因为它们会在视图加载的过程中才开始加载,并且如果在开发的时候没有做好正确的连接的时候它也会发生崩溃。我很希望能够在 Swift 演变列表中看到您提出这个问题。
问:我很喜欢 Objective-C 运行时以及其所提供的反射功能。这些功能在 Swift 当中变得非常局限;您对此有没有什么看法?
Daniel:这是一个很庞大的话题,因为大家都希望拥有可选的方法和可选的协议,就像我们在 Objective-C 当中拥有的那样。这些功能目前在 Swift 当中仍为实现。Objective-C 的一个很赞的特性就是动态性了,它允许我们检查某个对象是否能够对某个选择器 (selector) 作出回应,并且根本不必知晓它的相关信息。然而,Swift 的类型安全从一开始就减轻了这个需要,它希望您在编译的时候就确定哪些可用而哪些不可用,而不是在运行时进行检查。
因此,我并不希望在 Swift 通过检查一致性来使用可选的方法和协议,因为这个想法和类型安全格格不入。Apple 的反应很强硬:您应当知道某个对象应该能够做什么事——这是您应当为此负责的事情。相反在 Objective-C 中,我可以在运行时对任何我想要处理的对象进行交换操作。因此,我认为直接将 Objective-C 翻译为 Objective-C 会导致我们遇上许多问题。因此我们不能直接对 Objective-C 进行翻译,我们应当重新思考一下我们的架构,然后适应 Swift 类型安全的架构。
我知道 Cocoa Touch API 通常会依赖于这个动态特性,因为它是使用 Objective-C 编写的,因此我希望以后我们能够拥有更 Swift 友好的 API 版本,我们就不必将 Objective-C 完全地翻译为 Swift 了。
不过我们现在可以想想,Objective-C 比以前要少了很多动态性,这几年来,Apple 一直在把我们往 Swift 的路上逼,比如说 id 指针现在变成了 UIButton 之类的事务,泛型的出现允许我们能够更加精确地指明类型。
问:在 Cocoa API 当中,有没有一种说法:要自动将古老的委托模式转变为闭包?
Daniel:Apple 这四年来一直在推动这个方向的进展。三年前的 WWDC 上,Apple 的 Modern Objective-C 的讲演鼓励开发者传递闭包(Objective-C 当中的代码块),而不是使用委托。他们希望我们自行提供对象的行为,反对使用依赖状态回调。
问:Swift 3 上的 KVO 目前现状如何?
Daniel:和 Swift 2 基本上是一样的,没有任何变化。如果您想使用键值观察功能的话,您需要继承 NSObject 。这并不是一种语言特性,而是一个由 NSObject 提供功能的库,因此这使得 KVO只能对一个类使用,而不是使用一个结构体或者枚举。也就是说,您可以写一大堆恶心的 didSet 和 willSet ,这样也能够得到和 KVO 相同的体验。
问:有没有一种好的方法来跟上 Swift 演变的邮件列表,只要不是一行一行地读就行?
Daniel:Erica Sadun 对您有很大帮助。她经常对这个列表进行总结:『这里有新的东西了。这里有一个正在活跃讨论的话题』。我想说的是她现在正在为社区提供一个极为重要的服务。她的博客在 这里 ,如果您在 她的网站 订阅了她的周刊的话,那么她的博客将是一个极好的消息来源。
编者按: 另一个很好的消息来源是 Jesse Squires 的 Swift Weekly Brief ,他总结了 Swift repo 的最近进展、能够进行贡献的初创项目、提案进程的最新进展、以及邮件列表的热点内容。
See the discussion on Hacker News .
Daniel Steinberg
Daniel 是畅销书 A Swift Kickstart 以及 Developing iOS 7 Apps for iPad and iPhone (这本书是斯坦福大学推出的并且很受欢迎的 iTunes U 系列的官方参考书) 的作者。他从 iOS SDK 首次发布的时候,就开始为 iPhone 和 iPad 编写应用了,并且从 Mac OS 7 开始就已经在为 Mac 编写程序了。Daniel 和他的公司 Dim Sum Thinking 提供了 iPhone、Cocoa 以及 Swift 的教学和咨询服务。
最新教程周点击榜
微信扫一扫}

我要回帖

更多关于 swift print 参数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信